/***************************************************************************/
/*                                                                         */
/*  ttinterp.c                                                             */
/*                                                                         */
/*    TrueType bytecode interpreter (body).                                */
/*                                                                         */
/*  Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 by */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#include <ft2build.h>
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_CALC_H
#include FT_TRIGONOMETRY_H
#include FT_SYSTEM_H

#include "ttinterp.h"

#include "tterrors.h"


#ifdef TT_USE_BYTECODE_INTERPRETER


    #define TT_MULFIX FT_MulFix
    #define TT_MULDIV FT_MulDiv
    #define TT_MULDIV_NO_ROUND FT_MulDiv_No_Round


/*************************************************************************/
/*                                                                       */
/* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
/* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
/* messages during execution.                                            */
/*                                                                       */
    #undef  FT_COMPONENT
    #define FT_COMPONENT trace_ttinterp

/*************************************************************************/
/*                                                                       */
/* In order to detect infinite loops in the code, we set up a counter    */
/* within the run loop.  A single stroke of interpretation is now        */
/* limited to a maximal number of opcodes defined below.                 */
/*                                                                       */
    #define MAX_RUNNABLE_OPCODES 1000000L


/*************************************************************************/
/*                                                                       */
/* There are two kinds of implementations:                               */
/*                                                                       */
/* a. static implementation                                              */
/*                                                                       */
/*    The current execution context is a static variable, which fields   */
/*    are accessed directly by the interpreter during execution.  The    */
/*    context is named `cur'.                                            */
/*                                                                       */
/*    This version is non-reentrant, of course.                          */
/*                                                                       */
/* b. indirect implementation                                            */
/*                                                                       */
/*    The current execution context is passed to _each_ function as its  */
/*    first argument, and each field is thus accessed indirectly.        */
/*                                                                       */
/*    This version is fully re-entrant.                                  */
/*                                                                       */
/* The idea is that an indirect implementation may be slower to execute  */
/* on low-end processors that are used in some systems (like 386s or     */
/* even 486s).                                                           */
/*                                                                       */
/* As a consequence, the indirect implementation is now the default, as  */
/* its performance costs can be considered negligible in our context.    */
/* Note, however, that we kept the same source with macros because:      */
/*                                                                       */
/* - The code is kept very close in design to the Pascal code used for   */
/*   development.                                                        */
/*                                                                       */
/* - It's much more readable that way!                                   */
/*                                                                       */
/* - It's still open to experimentation and tuning.                      */
/*                                                                       */
/*************************************************************************/


    #ifndef TT_CONFIG_OPTION_STATIC_INTERPRETER /* indirect implementation */

        #define CUR (*exc)                      /* see ttobjs.h */

/*************************************************************************/
/*                                                                       */
/* This macro is used whenever `exec' is unused in a function, to avoid  */
/* stupid warnings from pedantic compilers.                              */
/*                                                                       */
        #define FT_UNUSED_EXEC FT_UNUSED(exc)

    #else                                       /* static implementation */

        #define CUR cur

        #define FT_UNUSED_EXEC int __dummy = __dummy

static
TT_ExecContextRec cur;      /* static exec. context variable */

/* apparently, we have a _lot_ of direct indexing when accessing  */
/* the static `cur', which makes the code bigger (due to all the  */
/* four bytes addresses).                                         */

    #endif /* TT_CONFIG_OPTION_STATIC_INTERPRETER */


/*************************************************************************/
/*                                                                       */
/* The instruction argument stack.                                       */
/*                                                                       */
    #define INS_ARG EXEC_OP_ FT_Long * args /* see ttobjs.h for EXEC_OP_ */


/*************************************************************************/
/*                                                                       */
/* This macro is used whenever `args' is unused in a function, to avoid  */
/* stupid warnings from pedantic compilers.                              */
/*                                                                       */
    #define FT_UNUSED_ARG FT_UNUSED_EXEC; FT_UNUSED(args)


/*************************************************************************/
/*                                                                       */
/* The following macros hide the use of EXEC_ARG and EXEC_ARG_ to        */
/* increase readability of the code.                                     */
/*                                                                       */
/*************************************************************************/


    #define SKIP_Code() \
    SkipCode(EXEC_ARG)

    #define GET_ShortIns() \
    GetShortIns(EXEC_ARG)

    #define NORMalize(x, y, v) \
    Normalize(EXEC_ARG_ x, y, v)

    #define SET_SuperRound(scale, flags) \
    SetSuperRound(EXEC_ARG_ scale, flags)

    #define ROUND_None(d, c) \
    Round_None(EXEC_ARG_ d, c)

    #define INS_Goto_CodeRange(range, ip) \
    Ins_Goto_CodeRange(EXEC_ARG_ range, ip)

    #define CUR_Func_move(z, p, d) \
    CUR.func_move(EXEC_ARG_ z, p, d)

    #define CUR_Func_move_orig(z, p, d) \
    CUR.func_move_orig(EXEC_ARG_ z, p, d)

    #define CUR_Func_round(d, c) \
    CUR.func_round(EXEC_ARG_ d, c)

    #define CUR_Func_read_cvt(index) \
    CUR.func_read_cvt(EXEC_ARG_ index)

    #define CUR_Func_write_cvt(index, val) \
    CUR.func_write_cvt(EXEC_ARG_ index, val)

    #define CUR_Func_move_cvt(index, val) \
    CUR.func_move_cvt(EXEC_ARG_ index, val)

    #define CURRENT_Ratio() \
    Current_Ratio(EXEC_ARG)

    #define CURRENT_Ppem() \
    Current_Ppem(EXEC_ARG)

    #define CUR_Ppem() \
    Cur_PPEM(EXEC_ARG)

    #define INS_SxVTL(a, b, c, d) \
    Ins_SxVTL(EXEC_ARG_ a, b, c, d)

    #define COMPUTE_Funcs() \
    Compute_Funcs(EXEC_ARG)

    #define COMPUTE_Round(a) \
    Compute_Round(EXEC_ARG_ a)

    #define COMPUTE_Point_Displacement(a, b, c, d) \
    Compute_Point_Displacement(EXEC_ARG_ a, b, c, d)

    #define MOVE_Zp2_Point(a, b, c, t) \
    Move_Zp2_Point(EXEC_ARG_ a, b, c, t)


    #define CUR_Func_project(v1, v2) \
    CUR.func_project(EXEC_ARG_(v1)->x - (v2)->x, (v1)->y - (v2)->y)

    #define CUR_Func_dualproj(v1, v2) \
    CUR.func_dualproj(EXEC_ARG_(v1)->x - (v2)->x, (v1)->y - (v2)->y)

    #define CUR_fast_project(v) \
    CUR.func_project(EXEC_ARG_(v)->x, (v)->y)

    #define CUR_fast_dualproj(v) \
    CUR.func_dualproj(EXEC_ARG_(v)->x, (v)->y)


/*************************************************************************/
/*                                                                       */
/* Instruction dispatch function, as used by the interpreter.            */
/*                                                                       */
typedef void (*TInstruction_Function)(INS_ARG);


/*************************************************************************/
/*                                                                       */
/* A simple bounds-checking macro.                                       */
/*                                                                       */
    #define BOUNDS(x, n)  ((FT_UInt)(x) >= (FT_UInt)(n))

    #undef  SUCCESS
    #define SUCCESS 0

    #undef  FAILURE
    #define FAILURE 1

    #ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
        #define GUESS_VECTOR(V) \
    if (CUR.face->unpatented_hinting) \
    { \
        CUR.GS.V.x = (FT_F2Dot14)(CUR.GS.both_x_axis ? 0x4000 : 0); \
        CUR.GS.V.y = (FT_F2Dot14)(CUR.GS.both_x_axis ? 0 : 0x4000); \
    }
    #else
        #define GUESS_VECTOR(V)
    #endif

/*************************************************************************/
/*                                                                       */
/*                        CODERANGE FUNCTIONS                            */
/*                                                                       */
/*************************************************************************/


/*************************************************************************/
/*                                                                       */
/* <Function>                                                            */
/*    TT_Goto_CodeRange                                                  */
/*                                                                       */
/* <Description>                                                         */
/*    Switches to a new code range (updates the code related elements in */
/*    `exec', and `IP').                                                 */
/*                                                                       */
/* <Input>                                                               */
/*    range :: The new execution code range.                             */
/*                                                                       */
/*    IP    :: The new IP in the new code range.                         */
/*                                                                       */
/* <InOut>                                                               */
/*    exec  :: The target execution context.                             */
/*                                                                       */
/* <Return>                                                              */
/*    FreeType error code.  0 means success.                             */
/*                                                                       */
FT_LOCAL_DEF(FT_Error)
TT_Goto_CodeRange(TT_ExecContext exec,
                  FT_Int range,
                  FT_Long IP)
{
    TT_CodeRange* coderange;


    FT_ASSERT(range >= 1 && range <= 3);

    coderange = &exec->codeRangeTable[range - 1];

    FT_ASSERT(coderange->base != NULL);

    /* NOTE: Because the last instruction of a program may be a CALL */
    /*       which will return to the first byte *after* the code    */
    /*       range, we test for IP <= Size instead of IP < Size.     */
    /*                                                               */
    FT_ASSERT((FT_ULong)IP <= coderange->size);

    exec->code = coderange->base;
    exec->codeSize = coderange->size;
    exec->IP = IP;
    exec->curRange = range;

    return TT_Err_Ok;
}


/*************************************************************************/
/*                                                                       */
/* <Function>                                                            */
/*    TT_Set_CodeRange                                                   */
/*                                                                       */
/* <Description>                                                         */
/*    Sets a code range.                                                 */
/*                                                                       */
/* <Input>                                                               */
/*    range  :: The code range index.                                    */
/*                                                                       */
/*    base   :: The new code base.                                       */
/*                                                                       */
/*    length :: The range size in bytes.                                 */
/*                                                                       */
/* <InOut>                                                               */
/*    exec   :: The target execution context.                            */
/*                                                                       */
/* <Return>                                                              */
/*    FreeType error code.  0 means success.                             */
/*                                                                       */
FT_LOCAL_DEF(FT_Error)
TT_Set_CodeRange(TT_ExecContext exec,
                 FT_Int range,
                 void* base,
                 FT_Long length)
{
    FT_ASSERT(range >= 1 && range <= 3);

    exec->codeRangeTable[range - 1].base = (FT_Byte*)base;
    exec->codeRangeTable[range - 1].size = length;

    return TT_Err_Ok;
}


/*************************************************************************/
/*                                                                       */
/* <Function>                                                            */
/*    TT_Clear_CodeRange                                                 */
/*                                                                       */
/* <Description>                                                         */
/*    Clears a code range.                                               */
/*                                                                       */
/* <Input>                                                               */
/*    range :: The code range index.                                     */
/*                                                                       */
/* <InOut>                                                               */
/*    exec  :: The target execution context.                             */
/*                                                                       */
/* <Return>                                                              */
/*    FreeType error code.  0 means success.                             */
/*                                                                       */
/* <Note>                                                                */
/*    Does not set the Error variable.                                   */
/*                                                                       */
FT_LOCAL_DEF(FT_Error)
TT_Clear_CodeRange(TT_ExecContext exec,
                   FT_Int range)
{
    FT_ASSERT(range >= 1 && range <= 3);

    exec->codeRangeTable[range - 1].base = NULL;
    exec->codeRangeTable[range - 1].size = 0;

    return TT_Err_Ok;
}


/*************************************************************************/
/*                                                                       */
/*                   EXECUTION CONTEXT ROUTINES                          */
/*                                                                       */
/*************************************************************************/


/*************************************************************************/
/*                                                                       */
/* <Function>                                                            */
/*    TT_Done_Context                                                    */
/*                                                                       */
/* <Description>                                                         */
/*    Destroys a given context.                                          */
/*                                                                       */
/* <Input>                                                               */
/*    exec   :: A handle to the target execution context.                */
/*                                                                       */
/*    memory :: A handle to the parent memory object.                    */
/*                                                                       */
/* <Return>                                                              */
/*    FreeType error code.  0 means success.                             */
/*                                                                       */
/* <Note>                                                                */
/*    Only the glyph loader and debugger should call this function.      */
/*                                                                       */
FT_LOCAL_DEF(FT_Error)
TT_Done_Context(TT_ExecContext exec)
{
    FT_Memory memory = exec->memory;


    /* points zone */
    exec->maxPoints = 0;
    exec->maxContours = 0;

    /* free stack */
    FT_FREE(exec->stack);
    exec->stackSize = 0;

    /* free call stack */
    FT_FREE(exec->callStack);
    exec->callSize = 0;
    exec->callTop = 0;

    /* free glyph code range */
    FT_FREE(exec->glyphIns);
    exec->glyphSize = 0;

    exec->size = NULL;
    exec->face = NULL;

    FT_FREE(exec);

    return TT_Err_Ok;
}


/*************************************************************************/
/*                                                                       */
/* <Function>                                                            */
/*    Init_Context                                                       */
/*                                                                       */
/* <Description>                                                         */
/*    Initializes a context object.                                      */
/*                                                                       */
/* <Input>                                                               */
/*    memory :: A handle to the parent memory object.                    */
/*                                                                       */
/* <InOut>                                                               */
/*    exec   :: A handle to the target execution context.                */
/*                                                                       */
/* <Return>                                                              */
/*    FreeType error code.  0 means success.                             */
/*                                                                       */
static FT_Error
Init_Context(TT_ExecContext exec,
             FT_Memory memory)
{
    FT_Error error;


    FT_TRACE1(("Init_Context: new object at 0x%08p\n", exec));

    exec->memory = memory;
    exec->callSize = 32;

    if (FT_NEW_ARRAY(exec->callStack, exec->callSize))
        goto Fail_Memory;

    /* all values in the context are set to 0 already, but this is */
    /* here as a remainder                                         */
    exec->maxPoints = 0;
    exec->maxContours = 0;

    exec->stackSize = 0;
    exec->glyphSize = 0;

    exec->stack = NULL;
    exec->glyphIns = NULL;

    exec->face = NULL;
    exec->size = NULL;

    return TT_Err_Ok;

Fail_Memory:
    FT_ERROR(("Init_Context: not enough memory for 0x%08lx\n",
              (FT_Long)exec));
    TT_Done_Context(exec);

    return error;
}


/*************************************************************************/
/*                                                                       */
/* <Function>                                                            */
/*    Update_Max                                                         */
/*                                                                       */
/* <Description>                                                         */
/*    Checks the size of a buffer and reallocates it if necessary.       */
/*                                                                       */
/* <Input>                                                               */
/*    memory     :: A handle to the parent memory object.                */
/*                                                                       */
/*    multiplier :: The size in bytes of each element in the buffer.     */
/*                                                                       */
/*    new_max    :: The new capacity (size) of the buffer.               */
/*                                                                       */
/* <InOut>                                                               */
/*    size       :: The address of the buffer's current size expressed   */
/*                  in elements.                                         */
/*                                                                       */
/*    buff       :: The address of the buffer base pointer.              */
/*                                                                       */
/* <Return>                                                              */
/*    FreeType error code.  0 means success.                             */
/*                                                                       */
static FT_Error
Update_Max(FT_Memory memory,
           FT_ULong* size,
           FT_Long multiplier,
           void* _pbuff,
           FT_ULong new_max)
{
    FT_Error error;
    void** pbuff = (void**)_pbuff;


    if (*size < new_max)
    {
        if (FT_REALLOC(*pbuff, *size * multiplier, new_max * multiplier))
            return error;
        *size = new_max;
    }

    return TT_Err_Ok;
}


/*************************************************************************/
/*                                                                       */
/* <Function>                                                            */
/*    TT_Load_Context                                                    */
/*                                                                       */
/* <Description>                                                         */
/*    Prepare an execution context for glyph hinting.                    */
/*                                                                       */
/* <Input>                                                               */
/*    face :: A handle to the source face object.                        */
/*                                                                       */
/*    size :: A handle to the source size object.                        */
/*                                                                       */
/* <InOut>                                                               */
/*    exec :: A handle to the target execution context.                  */
/*                                                                       */
/* <Return>                                                              */
/*    FreeType error code.  0 means success.                             */
/*                                                                       */
/* <Note>                                                                */
/*    Only the glyph loader and debugger should call this function.      */
/*                                                                       */
FT_LOCAL_DEF(FT_Error)
TT_Load_Context(TT_ExecContext exec,
                TT_Face face,
                TT_Size size)
{
    FT_Int i;
    FT_ULong tmp;
    TT_MaxProfile* maxp;
    FT_Error error;


    exec->face = face;
    maxp = &face->max_profile;
    exec->size = size;

    if (size)
    {
        exec->numFDefs = size->num_function_defs;
        exec->maxFDefs = size->max_function_defs;
        exec->numIDefs = size->num_instruction_defs;
        exec->maxIDefs = size->max_instruction_defs;
        exec->FDefs = size->function_defs;
        exec->IDefs = size->instruction_defs;
        exec->tt_metrics = size->ttmetrics;
        exec->metrics = size->metrics;

        exec->maxFunc = size->max_func;
        exec->maxIns = size->max_ins;

        for (i = 0; i < TT_MAX_CODE_RANGES; i++)
            exec->codeRangeTable[i] = size->codeRangeTable[i];

        /* set graphics state */
        exec->GS = size->GS;

        exec->cvtSize = size->cvt_size;
        exec->cvt = size->cvt;

        exec->storeSize = size->storage_size;
        exec->storage = size->storage;

        exec->twilight = size->twilight;
    }

    /* XXX: We reserve a little more elements on the stack to deal safely */
    /*      with broken fonts like arialbs, courbs, timesbs, etc.         */
    tmp = exec->stackSize;
    error = Update_Max(exec->memory,
                       &tmp,
                       sizeof(FT_F26Dot6),
                       (void*)&exec->stack,
                       maxp->maxStackElements + 32);
    exec->stackSize = (FT_UInt)tmp;
    if (error)
        return error;

    tmp = exec->glyphSize;
    error = Update_Max(exec->memory,
                       &tmp,
                       sizeof(FT_Byte),
                       (void*)&exec->glyphIns,
                       maxp->maxSizeOfInstructions);
    exec->glyphSize = (FT_UShort)tmp;
    if (error)
        return error;

    exec->pts.n_points = 0;
    exec->pts.n_contours = 0;

    exec->zp1 = exec->pts;
    exec->zp2 = exec->pts;
    exec->zp0 = exec->pts;

    exec->instruction_trap = FALSE;

    return TT_Err_Ok;
}


/*************************************************************************/
/*                                                                       */
/* <Function>                                                            */
/*    TT_Save_Context                                                    */
/*                                                                       */
/* <Description>                                                         */
/*    Saves the code ranges in a `size' object.                          */
/*                                                                       */
/* <Input>                                                               */
/*    exec :: A handle to the source execution context.                  */
/*                                                                       */
/* <InOut>                                                               */
/*    size :: A handle to the target size object.                        */
/*                                                                       */
/* <Return>                                                              */
/*    FreeType error code.  0 means success.                             */
/*                                                                       */
/* <Note>                                                                */
/*    Only the glyph loader and debugger should call this function.      */
/*                                                                       */
FT_LOCAL_DEF(FT_Error)
TT_Save_Context(TT_ExecContext exec,
                TT_Size size)
{
    FT_Int i;


    /* XXXX: Will probably disappear soon with all the code range */
    /*       management, which is now rather obsolete.            */
    /*                                                            */
    size->num_function_defs = exec->numFDefs;
    size->num_instruction_defs = exec->numIDefs;

    size->max_func = exec->maxFunc;
    size->max_ins = exec->maxIns;

    for (i = 0; i < TT_MAX_CODE_RANGES; i++)
        size->codeRangeTable[i] = exec->codeRangeTable[i];

    return TT_Err_Ok;
}


/*************************************************************************/
/*                                                                       */
/* <Function>                                                            */
/*    TT_Run_Context                                                     */
/*                                                                       */
/* <Description>                                                         */
/*    Executes one or more instructions in the execution context.        */
/*                                                                       */
/* <Input>                                                               */
/*    debug :: A Boolean flag.  If set, the function sets some internal  */
/*             variables and returns immediately, otherwise TT_RunIns()  */
/*             is called.                                                */
/*                                                                       */
/*             This is commented out currently.                          */
/*                                                                       */
/* <Input>                                                               */
/*    exec  :: A handle to the target execution context.                 */
/*                                                                       */
/* <Return>                                                              */
/*    TrueType error code.  0 means success.                             */
/*                                                                       */
/* <Note>                                                                */
/*    Only the glyph loader and debugger should call this function.      */
/*                                                                       */
FT_LOCAL_DEF(FT_Error)
TT_Run_Context(TT_ExecContext exec,
               FT_Bool debug)
{
    FT_Error error;


    if ((error = TT_Goto_CodeRange(exec, tt_coderange_glyph, 0))
        != TT_Err_Ok)
        return error;

    exec->zp0 = exec->pts;
    exec->zp1 = exec->pts;
    exec->zp2 = exec->pts;

    exec->GS.gep0 = 1;
    exec->GS.gep1 = 1;
    exec->GS.gep2 = 1;

    exec->GS.projVector.x = 0x4000;
    exec->GS.projVector.y = 0x0000;

    exec->GS.freeVector = exec->GS.projVector;
    exec->GS.dualVector = exec->GS.projVector;

    #ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
    exec->GS.both_x_axis = TRUE;
    #endif

    exec->GS.round_state = 1;
    exec->GS.loop = 1;

    /* some glyphs leave something on the stack. so we clean it */
    /* before a new execution.                                  */
    exec->top = 0;
    exec->callTop = 0;

    #if 1
    FT_UNUSED(debug);

    return exec->face->interpreter(exec);
    #else
    if (!debug)
        return TT_RunIns(exec);
    else
        return TT_Err_Ok;
    #endif
}


/* The default value for `scan_control' is documented as FALSE in the */
/* TrueType specification.  This is confusing since it implies a      */
/* Boolean value.  However, this is not the case, thus both the       */
/* default values of our `scan_type' and `scan_control' fields (which */
/* the documentation's `scan_control' variable is split into) are     */
/* zero.                                                              */

const TT_GraphicsState tt_default_graphics_state =
{
    0, 0, 0,
    { 0x4000, 0 },
    { 0x4000, 0 },
    { 0x4000, 0 },

    #ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
    TRUE,
    #endif

    1, 64, 1,
    TRUE, 68, 0, 0, 9, 3,
    0, FALSE, 0, 1, 1, 1
};


/* documentation is in ttinterp.h */

FT_EXPORT_DEF(TT_ExecContext)
TT_New_Context(TT_Driver driver)
{
    TT_ExecContext exec;
    FT_Memory memory;


    memory = driver->root.root.memory;
    exec = driver->context;

    if (!driver->context)
    {
        FT_Error error;


        /* allocate object */
        if (FT_NEW(exec))
            goto Fail;

        /* initialize it; in case of error this deallocates `exec' too */
        error = Init_Context(exec, memory);
        if (error)
            goto Fail;

        /* store it into the driver */
        driver->context = exec;
    }

    return driver->context;

Fail:
    return NULL;
}


/*************************************************************************/
/*                                                                       */
/* Before an opcode is executed, the interpreter verifies that there are */
/* enough arguments on the stack, with the help of the `Pop_Push_Count'  */
/* table.                                                                */
/*                                                                       */
/* For each opcode, the first column gives the number of arguments that  */
/* are popped from the stack; the second one gives the number of those   */
/* that are pushed in result.                                            */
/*                                                                       */
/* Opcodes which have a varying number of parameters in the data stream  */
/* (NPUSHB, NPUSHW) are handled specially; they have a negative value in */
/* the `opcode_length' table, and the value in `Pop_Push_Count' is set   */
/* to zero.                                                              */
/*                                                                       */
/*************************************************************************/


    #undef  PACK
    #define PACK(x, y)  ((x << 4) | y)


static
const FT_Byte Pop_Push_Count[256] =
{
    /* opcodes are gathered in groups of 16 */
    /* please keep the spaces as they are   */

    /*  SVTCA  y  */ PACK(0, 0),
    /*  SVTCA  x  */ PACK(0, 0),
    /*  SPvTCA y  */ PACK(0, 0),
    /*  SPvTCA x  */ PACK(0, 0),
    /*  SFvTCA y  */ PACK(0, 0),
    /*  SFvTCA x  */ PACK(0, 0),
    /*  SPvTL //  */ PACK(2, 0),
    /*  SPvTL +   */ PACK(2, 0),
    /*  SFvTL //  */ PACK(2, 0),
    /*  SFvTL +   */ PACK(2, 0),
    /*  SPvFS     */ PACK(2, 0),
    /*  SFvFS     */ PACK(2, 0),
    /*  GPV       */ PACK(0, 2),
    /*  GFV       */ PACK(0, 2),
    /*  SFvTPv    */ PACK(0, 0),
    /*  ISECT     */ PACK(5, 0),

    /*  SRP0      */ PACK(1, 0),
    /*  SRP1      */ PACK(1, 0),
    /*  SRP2      */ PACK(1, 0),
    /*  SZP0      */ PACK(1, 0),
    /*  SZP1      */ PACK(1, 0),
    /*  SZP2      */ PACK(1, 0),
    /*  SZPS      */ PACK(1, 0),
    /*  SLOOP     */ PACK(1, 0),
    /*  RTG       */ PACK(0, 0),
    /*  RTHG      */ PACK(0, 0),
    /*  SMD       */ PACK(1, 0),
    /*  ELSE      */ PACK(0, 0),
    /*  JMPR      */ PACK(1, 0),
    /*  SCvTCi    */ PACK(1, 0),
    /*  SSwCi     */ PACK(1, 0),
    /*  SSW       */ PACK(1, 0),

    /*  DUP       */ PACK(1, 2),
    /*  POP       */ PACK(1, 0),
    /*  CLEAR     */ PACK(0, 0),
    /*  SWAP      */ PACK(2, 2),
    /*  DEPTH     */ PACK(0, 1),
    /*  CINDEX    */ PACK(1, 1),
    /*  MINDEX    */ PACK(1, 0),
    /*  AlignPTS  */ PACK(2, 0),
    /*  INS_$28   */ PACK(0, 0),
    /*  UTP       */ PACK(1, 0),
    /*  LOOPCALL  */ PACK(2, 0),
    /*  CALL      */ PACK(1, 0),
    /*  FDEF      */ PACK(1, 0),
    /*  ENDF      */ PACK(0, 0),
    /*  MDAP[0]   */ PACK(1, 0),
    /*  MDAP[1]   */ PACK(1, 0),

    /*  IUP[0]    */ PACK(0, 0),
    /*  IUP[1]    */ PACK(0, 0),
    /*  SHP[0]    */ PACK(0, 0),
    /*  SHP[1]    */ PACK(0, 0),
    /*  SHC[0]    */ PACK(1, 0),
    /*  SHC[1]    */ PACK(1, 0),
    /*  SHZ[0]    */ PACK(1, 0),
    /*  SHZ[1]    */ PACK(1, 0),
    /*  SHPIX     */ PACK(1, 0),
    /*  IP        */ PACK(0, 0),
    /*  MSIRP[0]  */ PACK(2, 0),
    /*  MSIRP[1]  */ PACK(2, 0),
    /*  AlignRP   */ PACK(0, 0),
    /*  RTDG      */ PACK(0, 0),
    /*  MIAP[0]   */ PACK(2, 0),
    /*  MIAP[1]   */ PACK(2, 0),

    /*  NPushB    */ PACK(0, 0),
    /*  NPushW    */ PACK(0, 0),
    /*  WS        */ PACK(2, 0),
    /*  RS        */ PACK(1, 1),
    /*  WCvtP     */ PACK(2, 0),
    /*  RCvt      */ PACK(1, 1),
    /*  GC[0]     */ PACK(1, 1),
    /*  GC[1]     */ PACK(1, 1),
    /*  SCFS      */ PACK(2, 0),
    /*  MD[0]     */ PACK(2, 1),
    /*  MD[1]     */ PACK(2, 1),
    /*  MPPEM     */ PACK(0, 1),
    /*  MPS       */ PACK(0, 1),
    /*  FlipON    */ PACK(0, 0),
    /*  FlipOFF   */ PACK(0, 0),
    /*  DEBUG     */ PACK(1, 0),

    /*  LT        */ PACK(2, 1),
    /*  LTEQ      */ PACK(2, 1),
    /*  GT        */ PACK(2, 1),
    /*  GTEQ      */ PACK(2, 1),
    /*  EQ        */ PACK(2, 1),
    /*  NEQ       */ PACK(2, 1),
    /*  ODD       */ PACK(1, 1),
    /*  EVEN      */ PACK(1, 1),
    /*  IF        */ PACK(1, 0),
    /*  EIF       */ PACK(0, 0),
    /*  AND       */ PACK(2, 1),
    /*  OR        */ PACK(2, 1),
    /*  NOT       */ PACK(1, 1),
    /*  DeltaP1   */ PACK(1, 0),
    /*  SDB       */ PACK(1, 0),
    /*  SDS       */ PACK(1, 0),

    /*  ADD       */ PACK(2, 1),
    /*  SUB       */ PACK(2, 1),
    /*  DIV       */ PACK(2, 1),
    /*  MUL       */ PACK(2, 1),
    /*  ABS       */ PACK(1, 1),
    /*  NEG       */ PACK(1, 1),
    /*  FLOOR     */ PACK(1, 1),
    /*  CEILING   */ PACK(1, 1),
    /*  ROUND[0]  */ PACK(1, 1),
    /*  ROUND[1]  */ PACK(1, 1),
    /*  ROUND[2]  */ PACK(1, 1),
    /*  ROUND[3]  */ PACK(1, 1),
    /*  NROUND[0] */ PACK(1, 1),
    /*  NROUND[1] */ PACK(1, 1),
    /*  NROUND[2] */ PACK(1, 1),
    /*  NROUND[3] */ PACK(1, 1),

    /*  WCvtF     */ PACK(2, 0),
    /*  DeltaP2   */ PACK(1, 0),
    /*  DeltaP3   */ PACK(1, 0),
    /*  DeltaCn[0] */ PACK(1, 0),
    /*  DeltaCn[1] */ PACK(1, 0),
    /*  DeltaCn[2] */ PACK(1, 0),
    /*  SROUND    */ PACK(1, 0),
    /*  S45Round  */ PACK(1, 0),
    /*  JROT      */ PACK(2, 0),
    /*  JROF      */ PACK(2, 0),
    /*  ROFF      */ PACK(0, 0),
    /*  INS_$7B   */ PACK(0, 0),
    /*  RUTG      */ PACK(0, 0),
    /*  RDTG      */ PACK(0, 0),
    /*  SANGW     */ PACK(1, 0),
    /*  AA        */ PACK(1, 0),

    /*  FlipPT    */ PACK(0, 0),
    /*  FlipRgON  */ PACK(2, 0),
    /*  FlipRgOFF */ PACK(2, 0),
    /*  INS_$83   */ PACK(0, 0),
    /*  INS_$84   */ PACK(0, 0),
    /*  ScanCTRL  */ PACK(1, 0),
    /*  SDVPTL[0] */ PACK(2, 0),
    /*  SDVPTL[1] */ PACK(2, 0),
    /*  GetINFO   */ PACK(1, 1),
    /*  IDEF      */ PACK(1, 0),
    /*  ROLL      */ PACK(3, 3),
    /*  MAX       */ PACK(2, 1),
    /*  MIN       */ PACK(2, 1),
    /*  ScanTYPE  */ PACK(1, 0),
    /*  InstCTRL  */ PACK(2, 0),
    /*  INS_$8F   */ PACK(0, 0),

    /*  INS_$90  */ PACK(0, 0),
    /*  INS_$91  */ PACK(0, 0),
    /*  INS_$92  */ PACK(0, 0),
    /*  INS_$93  */ PACK(0, 0),
    /*  INS_$94  */ PACK(0, 0),
    /*  INS_$95  */ PACK(0, 0),
    /*  INS_$96  */ PACK(0, 0),
    /*  INS_$97  */ PACK(0, 0),
    /*  INS_$98  */ PACK(0, 0),
    /*  INS_$99  */ PACK(0, 0),
    /*  INS_$9A  */ PACK(0, 0),
    /*  INS_$9B  */ PACK(0, 0),
    /*  INS_$9C  */ PACK(0, 0),
    /*  INS_$9D  */ PACK(0, 0),
    /*  INS_$9E  */ PACK(0, 0),
    /*  INS_$9F  */ PACK(0, 0),

    /*  INS_$A0  */ PACK(0, 0),
    /*  INS_$A1  */ PACK(0, 0),
    /*  INS_$A2  */ PACK(0, 0),
    /*  INS_$A3  */ PACK(0, 0),
    /*  INS_$A4  */ PACK(0, 0),
    /*  INS_$A5  */ PACK(0, 0),
    /*  INS_$A6  */ PACK(0, 0),
    /*  INS_$A7  */ PACK(0, 0),
    /*  INS_$A8  */ PACK(0, 0),
    /*  INS_$A9  */ PACK(0, 0),
    /*  INS_$AA  */ PACK(0, 0),
    /*  INS_$AB  */ PACK(0, 0),
    /*  INS_$AC  */ PACK(0, 0),
    /*  INS_$AD  */ PACK(0, 0),
    /*  INS_$AE  */ PACK(0, 0),
    /*  INS_$AF  */ PACK(0, 0),

    /*  PushB[0]  */ PACK(0, 1),
    /*  PushB[1]  */ PACK(0, 2),
    /*  PushB[2]  */ PACK(0, 3),
    /*  PushB[3]  */ PACK(0, 4),
    /*  PushB[4]  */ PACK(0, 5),
    /*  PushB[5]  */ PACK(0, 6),
    /*  PushB[6]  */ PACK(0, 7),
    /*  PushB[7]  */ PACK(0, 8),
    /*  PushW[0]  */ PACK(0, 1),
    /*  PushW[1]  */ PACK(0, 2),
    /*  PushW[2]  */ PACK(0, 3),
    /*  PushW[3]  */ PACK(0, 4),
    /*  PushW[4]  */ PACK(0, 5),
    /*  PushW[5]  */ PACK(0, 6),
    /*  PushW[6]  */ PACK(0, 7),
    /*  PushW[7]  */ PACK(0, 8),

    /*  MDRP[00]  */ PACK(1, 0),
    /*  MDRP[01]  */ PACK(1, 0),
    /*  MDRP[02]  */ PACK(1, 0),
    /*  MDRP[03]  */ PACK(1, 0),
    /*  MDRP[04]  */ PACK(1, 0),
    /*  MDRP[05]  */ PACK(1, 0),
    /*  MDRP[06]  */ PACK(1, 0),
    /*  MDRP[07]  */ PACK(1, 0),
    /*  MDRP[08]  */ PACK(1, 0),
    /*  MDRP[09]  */ PACK(1, 0),
    /*  MDRP[10]  */ PACK(1, 0),
    /*  MDRP[11]  */ PACK(1, 0),
    /*  MDRP[12]  */ PACK(1, 0),
    /*  MDRP[13]  */ PACK(1, 0),
    /*  MDRP[14]  */ PACK(1, 0),
    /*  MDRP[15]  */ PACK(1, 0),

    /*  MDRP[16]  */ PACK(1, 0),
    /*  MDRP[17]  */ PACK(1, 0),
    /*  MDRP[18]  */ PACK(1, 0),
    /*  MDRP[19]  */ PACK(1, 0),
    /*  MDRP[20]  */ PACK(1, 0),
    /*  MDRP[21]  */ PACK(1, 0),
    /*  MDRP[22]  */ PACK(1, 0),
    /*  MDRP[23]  */ PACK(1, 0),
    /*  MDRP[24]  */ PACK(1, 0),
    /*  MDRP[25]  */ PACK(1, 0),
    /*  MDRP[26]  */ PACK(1, 0),
    /*  MDRP[27]  */ PACK(1, 0),
    /*  MDRP[28]  */ PACK(1, 0),
    /*  MDRP[29]  */ PACK(1, 0),
    /*  MDRP[30]  */ PACK(1, 0),
    /*  MDRP[31]  */ PACK(1, 0),

    /*  MIRP[00]  */ PACK(2, 0),
    /*  MIRP[01]  */ PACK(2, 0),
    /*  MIRP[02]  */ PACK(2, 0),
    /*  MIRP[03]  */ PACK(2, 0),
    /*  MIRP[04]  */ PACK(2, 0),
    /*  MIRP[05]  */ PACK(2, 0),
    /*  MIRP[06]  */ PACK(2, 0),
    /*  MIRP[07]  */ PACK(2, 0),
    /*  MIRP[08]  */ PACK(2, 0),
    /*  MIRP[09]  */ PACK(2, 0),
    /*  MIRP[10]  */ PACK(2, 0),
    /*  MIRP[11]  */ PACK(2, 0),
    /*  MIRP[12]  */ PACK(2, 0),
    /*  MIRP[13]  */ PACK(2, 0),
    /*  MIRP[14]  */ PACK(2, 0),
    /*  MIRP[15]  */ PACK(2, 0),

    /*  MIRP[16]  */ PACK(2, 0),
    /*  MIRP[17]  */ PACK(2, 0),
    /*  MIRP[18]  */ PACK(2, 0),
    /*  MIRP[19]  */ PACK(2, 0),
    /*  MIRP[20]  */ PACK(2, 0),
    /*  MIRP[21]  */ PACK(2, 0),
    /*  MIRP[22]  */ PACK(2, 0),
    /*  MIRP[23]  */ PACK(2, 0),
    /*  MIRP[24]  */ PACK(2, 0),
    /*  MIRP[25]  */ PACK(2, 0),
    /*  MIRP[26]  */ PACK(2, 0),
    /*  MIRP[27]  */ PACK(2, 0),
    /*  MIRP[28]  */ PACK(2, 0),
    /*  MIRP[29]  */ PACK(2, 0),
    /*  MIRP[30]  */ PACK(2, 0),
    /*  MIRP[31]  */ PACK(2, 0)
};


static
const FT_Char opcode_length[256] =
{
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,

    -1, -2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,

    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    2, 3, 4, 5, 6, 7, 8, 9, 3, 5, 7, 9, 11, 13, 15, 17,

    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
};

    #undef PACK

    #if 1

static FT_Int32
TT_MulFix14(FT_Int32 a,
            FT_Int b)
{
    FT_Int32 sign;
    FT_UInt32 ah, al, mid, lo, hi;


    sign = a ^ b;

    if (a < 0)
        a = -a;
    if (b < 0)
        b = -b;

    ah = (FT_UInt32)((a >> 16) & 0xFFFFU);
    al = (FT_UInt32)(a & 0xFFFFU);

    lo = al * b;
    mid = ah * b;
    hi = mid >> 16;
    mid = (mid << 16) + (1 << 13);       /* rounding */
    lo += mid;
    if (lo < mid)
        hi += 1;

    mid = (lo >> 14) | (hi << 18);

    return sign >= 0 ? (FT_Int32)mid : -(FT_Int32)mid;
}

    #else

/* compute (a*b)/2^14 with maximal accuracy and rounding */
static FT_Int32
TT_MulFix14(FT_Int32 a,
            FT_Int b)
{
    FT_Int32 m, s, hi;
    FT_UInt32 l, lo;


    /* compute ax*bx as 64-bit value */
    l = (FT_UInt32)((a & 0xFFFFU) * b);
    m = (a >> 16) * b;

    lo = l + (FT_UInt32)(m << 16);
    hi = (m >> 16) + ((FT_Int32)l >> 31) + (lo < l);

    /* divide the result by 2^14 with rounding */
    s = hi >> 31;
    l = lo + (FT_UInt32)s;
    hi += s + (l < lo);
    lo = l;

    l = lo + 0x2000U;
    hi += l < lo;

    return (hi << 18) | (l >> 14);
}
    #endif


/* compute (ax*bx+ay*by)/2^14 with maximal accuracy and rounding */
static FT_Int32
TT_DotFix14(FT_Int32 ax,
            FT_Int32 ay,
            FT_Int bx,
            FT_Int by)
{
    FT_Int32 m, s, hi1, hi2, hi;
    FT_UInt32 l, lo1, lo2, lo;


    /* compute ax*bx as 64-bit value */
    l = (FT_UInt32)((ax & 0xFFFFU) * bx);
    m = (ax >> 16) * bx;

    lo1 = l + (FT_UInt32)(m << 16);
    hi1 = (m >> 16) + ((FT_Int32)l >> 31) + (lo1 < l);

    /* compute ay*by as 64-bit value */
    l = (FT_UInt32)((ay & 0xFFFFU) * by);
    m = (ay >> 16) * by;

    lo2 = l + (FT_UInt32)(m << 16);
    hi2 = (m >> 16) + ((FT_Int32)l >> 31) + (lo2 < l);

    /* add them */
    lo = lo1 + lo2;
    hi = hi1 + hi2 + (lo < lo1);

    /* divide the result by 2^14 with rounding */
    s = hi >> 31;
    l = lo + (FT_UInt32)s;
    hi += s + (l < lo);
    lo = l;

    l = lo + 0x2000U;
    hi += (l < lo);

    return (hi << 18) | (l >> 14);
}


/* return length of given vector */

    #if 0

static FT_Int32
TT_VecLen(FT_Int32 x,
          FT_Int32 y)
{
    FT_Int32 m, hi1, hi2, hi;
    FT_UInt32 l, lo1, lo2, lo;


    /* compute x*x as 64-bit value */
    lo = (FT_UInt32)(x & 0xFFFFU);
    hi = x >> 16;

    l = lo * lo;
    m = hi * lo;
    hi = hi * hi;

    lo1 = l + (FT_UInt32)(m << 17);
    hi1 = hi + (m >> 15) + (lo1 < l);

    /* compute y*y as 64-bit value */
    lo = (FT_UInt32)(y & 0xFFFFU);
    hi = y >> 16;

    l = lo * lo;
    m = hi * lo;
    hi = hi * hi;

    lo2 = l + (FT_UInt32)(m << 17);
    hi2 = hi + (m >> 15) + (lo2 < l);

    /* add them to get 'x*x+y*y' as 64-bit value */
    lo = lo1 + lo2;
    hi = hi1 + hi2 + (lo < lo1);

    /* compute the square root of this value */
    {
        FT_UInt32 root, rem, test_div;
        FT_Int count;


        root = 0;

        {
            rem = 0;
            count = 32;
            do
            {
                rem = (rem << 2) | ((FT_UInt32)hi >> 30);
                hi = (hi << 2) | (lo >> 30);
                lo <<= 2;
                root <<= 1;
                test_div = (root << 1) + 1;

                if (rem >= test_div)
                {
                    rem -= test_div;
                    root += 1;
                }
            }
            while (--count);
        }

        return (FT_Int32)root;
    }
}

    #else

/* this version uses FT_Vector_Length which computes the same value */
/* much, much faster..                                              */
/*                                                                  */
static FT_F26Dot6
TT_VecLen(FT_F26Dot6 X,
          FT_F26Dot6 Y)
{
    FT_Vector v;


    v.x = X;
    v.y = Y;

    return FT_Vector_Length(&v);
}

    #endif


/*************************************************************************/
/*                                                                       */
/* <Function>                                                            */
/*    Current_Ratio                                                      */
/*                                                                       */
/* <Description>                                                         */
/*    Returns the current aspect ratio scaling factor depending on the   */
/*    projection vector's state and device resolutions.                  */
/*                                                                       */
/* <Return>                                                              */
/*    The aspect ratio in 16.16 format, always <= 1.0 .                  */
/*                                                                       */
static FT_Long
Current_Ratio(EXEC_OP)
{
    if (!CUR.tt_metrics.ratio)
    {
        #ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
        if (CUR.face->unpatented_hinting)
        {
            if (CUR.GS.both_x_axis)
                CUR.tt_metrics.ratio = CUR.tt_metrics.x_ratio;
            else
                CUR.tt_metrics.ratio = CUR.tt_metrics.y_ratio;
        }
        else
        #endif
        {
            if (CUR.GS.projVector.y == 0)
                CUR.tt_metrics.ratio = CUR.tt_metrics.x_ratio;

            else if (CUR.GS.projVector.x == 0)
                CUR.tt_metrics.ratio = CUR.tt_metrics.y_ratio;

            else
            {
                FT_Long x, y;


                x = TT_MULDIV(CUR.GS.projVector.x,
                              CUR.tt_metrics.x_ratio, 0x4000);
                y = TT_MULDIV(CUR.GS.projVector.y,
                              CUR.tt_metrics.y_ratio, 0x4000);
                CUR.tt_metrics.ratio = TT_VecLen(x, y);
            }
        }
    }
    return CUR.tt_metrics.ratio;
}


static FT_Long
Current_Ppem(EXEC_OP)
{
    return TT_MULFIX(CUR.tt_metrics.ppem, CURRENT_Ratio());
}


/*************************************************************************/
/*                                                                       */
/* Functions related to the control value table (CVT).                   */
/*                                                                       */
/*************************************************************************/


FT_CALLBACK_DEF(FT_F26Dot6)
Read_CVT(EXEC_OP_ FT_ULong idx)
{
    return CUR.cvt[idx];
}


FT_CALLBACK_DEF(FT_F26Dot6)
Read_CVT_Stretched(EXEC_OP_ FT_ULong idx)
{
    return TT_MULFIX(CUR.cvt[idx], CURRENT_Ratio());
}


FT_CALLBACK_DEF(void)
Write_CVT(EXEC_OP_ FT_ULong idx,
          FT_F26Dot6 value)
{
    CUR.cvt[idx] = value;
}


FT_CALLBACK_DEF(void)
Write_CVT_Stretched(EXEC_OP_ FT_ULong idx,
                    FT_F26Dot6 value)
{
    CUR.cvt[idx] = FT_DivFix(value, CURRENT_Ratio());
}


FT_CALLBACK_DEF(void)
Move_CVT(EXEC_OP_ FT_ULong idx,
         FT_F26Dot6 value)
{
    CUR.cvt[idx] += value;
}


FT_CALLBACK_DEF(void)
Move_CVT_Stretched(EXEC_OP_ FT_ULong idx,
                   FT_F26Dot6 value)
{
    CUR.cvt[idx] += FT_DivFix(value, CURRENT_Ratio());
}


/*************************************************************************/
/*                                                                       */
/* <Function>                                                            */
/*    GetShortIns                                                        */
/*                                                                       */
/* <Description>                                                         */
/*    Returns a short integer taken from the instruction stream at       */
/*    address IP.                                                        */
/*                                                                       */
/* <Return>                                                              */
/*    Short read at code[IP].                                            */
/*                                                                       */
/* <Note>                                                                */
/*    This one could become a macro.                                     */
/*                                                                       */
static FT_Short
GetShortIns(EXEC_OP)
{
    /* Reading a byte stream so there is no endianess (DaveP) */
    CUR.IP += 2;
    return (FT_Short)((CUR.code[CUR.IP - 2] << 8) +
                      CUR.code[CUR.IP - 1]);
}


/*************************************************************************/
/*                                                                       */
/* <Function>                                                            */
/*    Ins_Goto_CodeRange                                                 */
/*                                                                       */
/* <Description>                                                         */
/*    Goes to a certain code range in the instruction stream.            */
/*                                                                       */
/* <Input>                                                               */
/*    aRange :: The index of the code range.                             */
/*                                                                       */
/*    aIP    :: The new IP address in the code range.                    */
/*                                                                       */
/* <Return>                                                              */
/*    SUCCESS or FAILURE.                                                */
/*                                                                       */
static FT_Bool
Ins_Goto_CodeRange(EXEC_OP_ FT_Int aRange,
                   FT_ULong aIP)
{
    TT_CodeRange* range;


    if (aRange < 1 || aRange > 3)
    {
        CUR.error = TT_Err_Bad_Argument;
        return FAILURE;
    }

    range = &CUR.codeRangeTable[aRange - 1];

    if (range->base == NULL)       /* invalid coderange */
    {
        CUR.error = TT_Err_Invalid_CodeRange;
        return FAILURE;
    }

    /* NOTE: Because the last instruction of a program may be a CALL */
    /*       which will return to the first byte *after* the code    */
    /*       range, we test for AIP <= Size, instead of AIP < Size.  */

    if (aIP > range->size)
    {
        CUR.error = TT_Err_Code_Overflow;
        return FAILURE;
    }

    CUR.code = range->base;
    CUR.codeSize = range->size;
    CUR.IP = aIP;
    CUR.curRange = aRange;

    return SUCCESS;
}


/*************************************************************************/
/*                                                                       */
/* <Function>                                                            */
/*    Direct_Move                                                        */
/*                                                                       */
/* <Description>                                                         */
/*    Moves a point by a given distance along the freedom vector.  The   */
/*    point will be `touched'.                                           */
/*                                                                       */
/* <Input>                                                               */
/*    point    :: The index of the point to move.                        */
/*                                                                       */
/*    distance :: The distance to apply.                                 */
/*                                                                       */
/* <InOut>                                                               */
/*    zone     :: The affected glyph zone.                               */
/*                                                                       */
static void
Direct_Move(EXEC_OP_ TT_GlyphZone zone,
            FT_UShort point,
            FT_F26Dot6 distance)
{
    FT_F26Dot6 v;


    #ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
    FT_ASSERT(!CUR.face->unpatented_hinting);
    #endif

    v = CUR.GS.freeVector.x;

    if (v != 0)
    {
        zone->cur[point].x += TT_MULDIV(distance,
                                        v * 0x10000L,
                                        CUR.F_dot_P);

        zone->tags[point] |= FT_CURVE_TAG_TOUCH_X;
    }

    v = CUR.GS.freeVector.y;

    if (v != 0)
    {
        zone->cur[point].y += TT_MULDIV(distance,
                                        v * 0x10000L,
                                        CUR.F_dot_P);

        zone->tags[point] |= FT_CURVE_TAG_TOUCH_Y;
    }
}


/*************************************************************************/
/*                                                                       */
/* <Function>                                                            */
/*    Direct_Move_Orig                                                   */
/*                                                                       */
/* <Description>                                                         */
/*    Moves the *original* position of a point by a given distance along */
/*    the freedom vector.  Obviously, the point will not be `touched'.   */
/*                                                                       */
/* <Input>                                                               */
/*    point    :: The index of the point to move.                        */
/*                                                                       */
/*    distance :: The distance to apply.                                 */
/*                                                                       */
/* <InOut>                                                               */
/*    zone     :: The affected glyph zone.                               */
/*                                                                       */
static void
Direct_Move_Orig(EXEC_OP_ TT_GlyphZone zone,
                 FT_UShort point,
                 FT_F26Dot6 distance)
{
    FT_F26Dot6 v;


    #ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
    FT_ASSERT(!CUR.face->unpatented_hinting);
    #endif

    v = CUR.GS.freeVector.x;

    if (v != 0)
        zone->org[point].x += TT_MULDIV(distance,
                                        v * 0x10000L,
                                        CUR.F_dot_P);

    v = CUR.GS.freeVector.y;

    if (v != 0)
        zone->org[point].y += TT_MULDIV(distance,
                                        v * 0x10000L,
                                        CUR.F_dot_P);
}


/*************************************************************************/
/*                                                                       */
/* Special versions of Direct_Move()                                     */
/*                                                                       */
/*   The following versions are used whenever both vectors are both      */
/*   along one of the coordinate unit vectors, i.e. in 90% of the cases. */
/*                                                                       */
/*************************************************************************/


static void
Direct_Move_X(EXEC_OP_ TT_GlyphZone zone,
              FT_UShort point,
              FT_F26Dot6 distance)
{
    FT_UNUSED_EXEC;

    zone->cur[point].x += distance;
    zone->tags[point] |= FT_CURVE_TAG_TOUCH_X;
}


static void
Direct_Move_Y(EXEC_OP_ TT_GlyphZone zone,
              FT_UShort point,
              FT_F26Dot6 distance)
{
    FT_UNUSED_EXEC;

    zone->cur[point].y += distance;
    zone->tags[point] |= FT_CURVE_TAG_TOUCH_Y;
}


/*************************************************************************/
/*                                                                       */
/* Special versions of Direct_Move_Orig()                                */
/*                                                                       */
/*   The following versions are used whenever both vectors are both      */
/*   along one of the coordinate unit vectors, i.e. in 90% of the cases. */
/*                                                                       */
/*************************************************************************/


static void
Direct_Move_Orig_X(EXEC_OP_ TT_GlyphZone zone,
                   FT_UShort point,
                   FT_F26Dot6 distance)
{
    FT_UNUSED_EXEC;

    zone->org[point].x += distance;
}


static void
Direct_Move_Orig_Y(EXEC_OP_ TT_GlyphZone zone,
                   FT_UShort point,
                   FT_F26Dot6 distance)
{
    FT_UNUSED_EXEC;

    zone->org[point].y += distance;
}


/*************************************************************************/
/*                                                                       */
/* <Function>                                                            */
/*    Round_None                                                         */
/*                                                                       */
/* <Description>                                                         */
/*    Does not round, but adds engine compensation.                      */
/*                                                                       */
/* <Input>                                                               */
/*    distance     :: The distance (not) to round.                       */
/*                                                                       */
/*    compensation :: The engine compensation.                           */
/*                                                                       */
/* <Return>                                                              */
/*    The compensated distance.                                          */
/*                                                                       */
/* <Note>                                                                */
/*    The TrueType specification says very few about the relationship    */
/*    between rounding and engine compensation.  However, it seems from  */
/*    the description of super round that we should add the compensation */
/*    before rounding.                                                   */
/*                                                                       */
static FT_F26Dot6
Round_None(EXEC_OP_ FT_F26Dot6 distance,
           FT_F26Dot6 compensation)
{
    FT_F26Dot6 val;

    FT_UNUSED_EXEC;


    if (distance >= 0)
    {
        val = distance + compensation;
        if (distance && val < 0)
            val = 0;
    }
    else
    {
        val = distance - compensation;
        if (val > 0)
            val = 0;
    }
    return val;
}


/*************************************************************************/
/*                                                                       */
/* <Function>                                                            */
/*    Round_To_Grid                                                      */
/*                                                                       */
/* <Description>                                                         */
/*    Rounds value to grid after adding engine compensation.             */
/*                                                                       */
/* <Input>                                                               */
/*    distance     :: The distance to round.                             */
/*                                                                       */
/*    compensation :: The engine compensation.                           */
/*                                                                       */
/* <Return>                                                              */
/*    Rounded distance.                                                  */
/*                                                                       */
static FT_F26Dot6
Round_To_Grid(EXEC_OP_ FT_F26Dot6 distance,
              FT_F26Dot6 compensation)
{
    FT_F26Dot6 val;

    FT_UNUSED_EXEC;


    if (distance >= 0)
    {
        val = distance + compensation + 32;
        if (distance && val > 0)
            val &= ~63;
        else
            val = 0;
    }
    else
    {
        val = -FT_PIX_ROUND(compensation - distance);
        if (val > 0)
            val = 0;
    }

    return val;
}


/*************************************************************************/
/*                                                                       */
/* <Function>                                                            */
/*    Round_To_Half_Grid                                                 */
/*                                                                       */
/* <Description>                                                         */
/*    Rounds value to half grid after adding engine compensation.        */
/*                                                                       */
/* <Input>                                                               */
/*    distance     :: The distance to round.                             */
/*                                                                       */
/*    compensation :: The engine compensation.                           */
/*                                                                       */
/* <Return>                                                              */
/*    Rounded distance.                                                  */
/*                                                                       */
static FT_F26Dot6
Round_To_Half_Grid(EXEC_OP_ FT_F26Dot6 distance,
                   FT_F26Dot6 compensation)
{
    FT_F26Dot6 val;

    FT_UNUSED_EXEC;


    if (distance >= 0)
    {
        val = FT_PIX_FLOOR(distance + compensation) + 32;
        if (distance && val < 0)
            val = 0;
    }
    else
    {
        val = -(FT_PIX_FLOOR(compensation - distance) + 32);
        if (val > 0)
            val = 0;
    }

    return val;
}


/*************************************************************************/
/*                                                                       */
/* <Function>                                                            */
/*    Round_Down_To_Grid                                                 */
/*                                                                       */
/* <Description>                                                         */
/*    Rounds value down to grid after adding engine compensation.        */
/*                                                                       */
/* <Input>                                                               */
/*    distance     :: The distance to round.                             */
/*                                                                       */
/*    compensation :: The engine compensation.                           */
/*                                                                       */
/* <Return>                                                              */
/*    Rounded distance.                                                  */
/*                                                                       */
static FT_F26Dot6
Round_Down_To_Grid(EXEC_OP_ FT_F26Dot6 distance,
                   FT_F26Dot6 compensation)
{
    FT_F26Dot6 val;

    FT_UNUSED_EXEC;


    if (distance >= 0)
    {
        val = distance + compensation;
        if (distance && val > 0)
            val &= ~63;
        else
            val = 0;
    }
    else
    {
        val = -((compensation - distance) & - 64);
        if (val > 0)
            val = 0;
    }

    return val;
}


/*************************************************************************/
/*                                                                       */
/* <Function>                                                            */
/*    Round_Up_To_Grid                                                   */
/*                                                                       */
/* <Description>                                                         */
/*    Rounds value up to grid after adding engine compensation.          */
/*                                                                       */
/* <Input>                                                               */
/*    distance     :: The distance to round.                             */
/*                                                                       */
/*    compensation :: The engine compensation.                           */
/*                                                                       */
/* <Return>                                                              */
/*    Rounded distance.                                                  */
/*                                                                       */
static FT_F26Dot6
Round_Up_To_Grid(EXEC_OP_ FT_F26Dot6 distance,
                 FT_F26Dot6 compensation)
{
    FT_F26Dot6 val;

    FT_UNUSED_EXEC;


    if (distance >= 0)
    {
        val = distance + compensation + 63;
        if (distance && val > 0)
            val &= ~63;
        else
            val = 0;
    }
    else
    {
        val = -FT_PIX_CEIL(compensation - distance);
        if (val > 0)
            val = 0;
    }

    return val;
}


/*************************************************************************/
/*                                                                       */
/* <Function>                                                            */
/*    Round_To_Double_Grid                                               */
/*                                                                       */
/* <Description>                                                         */
/*    Rounds value to double grid after adding engine compensation.      */
/*                                                                       */
/* <Input>                                                               */
/*    distance     :: The distance to round.                             */
/*                                                                       */
/*    compensation :: The engine compensation.                           */
/*                                                                       */
/* <Return>                                                              */
/*    Rounded distance.                                                  */
/*                                                                       */
static FT_F26Dot6
Round_To_Double_Grid(EXEC_OP_ FT_F26Dot6 distance,
                     FT_F26Dot6 compensation)
{
    FT_F26Dot6 val;

    FT_UNUSED_EXEC;


    if (distance >= 0)
    {
        val = distance + compensation + 16;
        if (distance && val > 0)
            val &= ~31;
        else
            val = 0;
    }
    else
    {
        val = -FT_PAD_ROUND(compensation - distance, 32);
        if (val > 0)
            val = 0;
    }

    return val;
}


/*************************************************************************/
/*                                                                       */
/* <Function>                                                            */
/*    Round_Super                                                        */
/*                                                                       */
/* <Description>                                                         */
/*    Super-rounds value to grid after adding engine compensation.       */
/*                                                                       */
/* <Input>                                                               */
/*    distance     :: The distance to round.                             */
/*                                                                       */
/*    compensation :: The engine compensation.                           */
/*                                                                       */
/* <Return>                                                              */
/*    Rounded distance.                                                  */
/*                                                                       */
/* <Note>                                                                */
/*    The TrueType specification says very few about the relationship    */
/*    between rounding and engine compensation.  However, it seems from  */
/*    the description of super round that we should add the compensation */
/*    before rounding.                                                   */
/*                                                                       */
static FT_F26Dot6
Round_Super(EXEC_OP_ FT_F26Dot6 distance,
            FT_F26Dot6 compensation)
{
    FT_F26Dot6 val;


    if (distance >= 0)
    {
        val = (distance - CUR.phase + CUR.threshold + compensation) &
              - CUR.period;
        if (distance && val < 0)
            val = 0;
        val += CUR.phase;
    }
    else
    {
        val = -((CUR.threshold - CUR.phase - distance + compensation) &
                - CUR.period);
        if (val > 0)
            val = 0;
        val -= CUR.phase;
    }

    return val;
}


/*************************************************************************/
/*                                                                       */
/* <Function>                                                            */
/*    Round_Super_45                                                     */
/*                                                                       */
/* <Description>                                                         */
/*    Super-rounds value to grid after adding engine compensation.       */
/*                                                                       */
/* <Input>                                                               */
/*    distance     :: The distance to round.                             */
/*                                                                       */
/*    compensation :: The engine compensation.                           */
/*                                                                       */
/* <Return>                                                              */
/*    Rounded distance.                                                  */
/*                                                                       */
/* <Note>                                                                */
/*    There is a separate function for Round_Super_45() as we may need   */
/*    greater precision.                                                 */
/*                                                                       */
static FT_F26Dot6
Round_Super_45(EXEC_OP_ FT_F26Dot6 distance,
               FT_F26Dot6 compensation)
{
    FT_F26Dot6 val;


    if (distance >= 0)
    {
        val = ((distance - CUR.phase + CUR.threshold + compensation) /
               CUR.period) * CUR.period;
        if (distance && val < 0)
            val = 0;
        val += CUR.phase;
    }
    else
    {
        val = -(((CUR.threshold - CUR.phase - distance + compensation) /
                 CUR.period) * CUR.period);
        if (val > 0)
            val = 0;
        val -= CUR.phase;
    }

    return val;
}


/*************************************************************************/
/*                                                                       */
/* <Function>                                                            */
/*    Compute_Round                                                      */
/*                                                                       */
/* <Description>                                                         */
/*    Sets the rounding mode.                                            */
/*                                                                       */
/* <Input>                                                               */
/*    round_mode :: The rounding mode to be used.                        */
/*                                                                       */
static void
Compute_Round(EXEC_OP_ FT_Byte round_mode)
{
    switch (round_mode)
    {
        case TT_Round_Off:
            CUR.func_round = (TT_Round_Func)Round_None;
            break;

        case TT_Round_To_Grid:
            CUR.func_round = (TT_Round_Func)Round_To_Grid;
            break;

        case TT_Round_Up_To_Grid:
            CUR.func_round = (TT_Round_Func)Round_Up_To_Grid;
            break;

        case TT_Round_Down_To_Grid:
            CUR.func_round = (TT_Round_Func)Round_Down_To_Grid;
            break;

        case TT_Round_To_Half_Grid:
            CUR.func_round = (TT_Round_Func)Round_To_Half_Grid;
            break;

        case TT_Round_To_Double_Grid:
            CUR.func_round = (TT_Round_Func)Round_To_Double_Grid;
            break;

        case TT_Round_Super:
            CUR.func_round = (TT_Round_Func)Round_Super;
            break;

        case TT_Round_Super_45:
            CUR.func_round = (TT_Round_Func)Round_Super_45;
            break;
    }
}


/*************************************************************************/
/*                                                                       */
/* <Function>                                                            */
/*    SetSuperRound                                                      */
/*                                                                       */
/* <Description>                                                         */
/*    Sets Super Round parameters.                                       */
/*                                                                       */
/* <Input>                                                               */
/*    GridPeriod :: Grid period                                          */
/*    selector   :: SROUND opcode                                        */
/*                                                                       */
static void
SetSuperRound(EXEC_OP_ FT_F26Dot6 GridPeriod,
              FT_Long selector)
{
    switch ((FT_Int)(selector & 0xC0))
    {
        case 0:
            CUR.period = GridPeriod / 2;
            break;

        case 0x40:
            CUR.period = GridPeriod;
            break;

        case 0x80:
            CUR.period = GridPeriod * 2;
            break;

        /* This opcode is reserved, but... */

        case 0xC0:
            CUR.period = GridPeriod;
            break;
    }

    switch ((FT_Int)(selector & 0x30))
    {
        case 0:
            CUR.phase = 0;
            break;

        case 0x10:
            CUR.phase = CUR.period / 4;
            break;

        case 0x20:
            CUR.phase = CUR.period / 2;
            break;

        case 0x30:
            CUR.phase = CUR.period * 3 / 4;
            break;
    }

    if ((selector & 0x0F) == 0)
        CUR.threshold = CUR.period - 1;
    else
        CUR.threshold = ((FT_Int)(selector & 0x0F) - 4) * CUR.period / 8;

    CUR.period /= 256;
    CUR.phase /= 256;
    CUR.threshold /= 256;
}


/*************************************************************************/
/*                                                                       */
/* <Function>                                                            */
/*    Project                                                            */
/*                                                                       */
/* <Description>                                                         */
/*    Computes the projection of vector given by (v2-v1) along the       */
/*    current projection vector.                                         */
/*                                                                       */
/* <Input>                                                               */
/*    v1 :: First input vector.                                          */
/*    v2 :: Second input vector.                                         */
/*                                                                       */
/* <Return>                                                              */
/*    The distance in F26dot6 format.                                    */
/*                                                                       */
static FT_F26Dot6
Project(EXEC_OP_ FT_Pos dx,
        FT_Pos dy)
{
    #ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
    FT_ASSERT(!CUR.face->unpatented_hinting);
    #endif

    return TT_DotFix14((FT_UInt32)dx, (FT_UInt32)dy,
                       CUR.GS.projVector.x,
                       CUR.GS.projVector.y);
}


/*************************************************************************/
/*                                                                       */
/* <Function>                                                            */
/*    Dual_Project                                                       */
/*                                                                       */
/* <Description>                                                         */
/*    Computes the projection of the vector given by (v2-v1) along the   */
/*    current dual vector.                                               */
/*                                                                       */
/* <Input>                                                               */
/*    v1 :: First input vector.                                          */
/*    v2 :: Second input vector.                                         */
/*                                                                       */
/* <Return>                                                              */
/*    The distance in F26dot6 format.                                    */
/*                                                                       */
static FT_F26Dot6
Dual_Project(EXEC_OP_ FT_Pos dx,
             FT_Pos dy)
{
    return TT_DotFix14((FT_UInt32)dx, (FT_UInt32)dy,
                       CUR.GS.dualVector.x,
                       CUR.GS.dualVector.y);
}


/*************************************************************************/
/*                                                                       */
/* <Function>                                                            */
/*    Project_x                                                          */
/*                                                                       */
/* <Description>                                                         */
/*    Computes the projection of the vector given by (v2-v1) along the   */
/*    horizontal axis.                                                   */
/*                                                                       */
/* <Input>                                                               */
/*    v1 :: First input vector.                                          */
/*    v2 :: Second input vector.                                         */
/*                                                                       */
/* <Return>                                                              */
/*    The distance in F26dot6 format.                                    */
/*                                                                       */
static FT_F26Dot6
Project_x(EXEC_OP_ FT_Pos dx,
          FT_Pos dy)
{
    FT_UNUSED_EXEC;
    FT_UNUSED(dy);

    return dx;
}


/*************************************************************************/
/*                                                                       */
/* <Function>                                                            */
/*    Project_y                                                          */
/*                                                                       */
/* <Description>                                                         */
/*    Computes the projection of the vector given by (v2-v1) along the   */
/*    vertical axis.                                                     */
/*                                                                       */
/* <Input>                                                               */
/*    v1 :: First input vector.                                          */
/*    v2 :: Second input vector.                                         */
/*                                                                       */
/* <Return>                                                              */
/*    The distance in F26dot6 format.                                    */
/*                                                                       */
static FT_F26Dot6
Project_y(EXEC_OP_ FT_Pos dx,
          FT_Pos dy)
{
    FT_UNUSED_EXEC;
    FT_UNUSED(dx);

    return dy;
}


/*************************************************************************/
/*                                                                       */
/* <Function>                                                            */
/*    Compute_Funcs                                                      */
/*                                                                       */
/* <Description>                                                         */
/*    Computes the projection and movement function pointers according   */
/*    to the current graphics state.                                     */
/*                                                                       */
static void
Compute_Funcs(EXEC_OP)
{
    #ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
    if (CUR.face->unpatented_hinting)
    {
        /* If both vectors point rightwards along the x axis, set             */
        /* `both-x-axis' true, otherwise set it false.  The x values only     */
        /* need be tested because the vector has been normalised to a unit    */
        /* vector of length 0x4000 = unity.                                   */
        CUR.GS.both_x_axis = (FT_Bool)(CUR.GS.projVector.x == 0x4000 &&
                                       CUR.GS.freeVector.x == 0x4000);

        /* Throw away projection and freedom vector information */
        /* because the patents don't allow them to be stored.   */
        /* The relevant US Patents are 5155805 and 5325479.     */
        CUR.GS.projVector.x = 0;
        CUR.GS.projVector.y = 0;
        CUR.GS.freeVector.x = 0;
        CUR.GS.freeVector.y = 0;

        if (CUR.GS.both_x_axis)
        {
            CUR.func_project = Project_x;
            CUR.func_move = Direct_Move_X;
            CUR.func_move_orig = Direct_Move_Orig_X;
        }
        else
        {
            CUR.func_project = Project_y;
            CUR.func_move = Direct_Move_Y;
            CUR.func_move_orig = Direct_Move_Orig_Y;
        }

        if (CUR.GS.dualVector.x == 0x4000)
            CUR.func_dualproj = Project_x;
        else
        {
            if (CUR.GS.dualVector.y == 0x4000)
                CUR.func_dualproj = Project_y;
            else
                CUR.func_dualproj = Dual_Project;
        }

        /* Force recalculation of cached aspect ratio */
        CUR.tt_metrics.ratio = 0;

        return;
    }
    #endif /* TT_CONFIG_OPTION_UNPATENTED_HINTING */

    if (CUR.GS.freeVector.x == 0x4000)
        CUR.F_dot_P = CUR.GS.projVector.x * 0x10000L;
    else
    {
        if (CUR.GS.freeVector.y == 0x4000)
            CUR.F_dot_P = CUR.GS.projVector.y * 0x10000L;
        else
            CUR.F_dot_P = (FT_Long)CUR.GS.projVector.x * CUR.GS.freeVector.x * 4 +
                          (FT_Long)CUR.GS.projVector.y * CUR.GS.freeVector.y * 4;
    }

    if (CUR.GS.projVector.x == 0x4000)
        CUR.func_project = (TT_Project_Func)Project_x;
    else
    {
        if (CUR.GS.projVector.y == 0x4000)
            CUR.func_project = (TT_Project_Func)Project_y;
        else
            CUR.func_project = (TT_Project_Func)Project;
    }

    if (CUR.GS.dualVector.x == 0x4000)
        CUR.func_dualproj = (TT_Project_Func)Project_x;
    else
    {
        if (CUR.GS.dualVector.y == 0x4000)
            CUR.func_dualproj = (TT_Project_Func)Project_y;
        else
            CUR.func_dualproj = (TT_Project_Func)Dual_Project;
    }

    CUR.func_move = (TT_Move_Func)Direct_Move;
    CUR.func_move_orig = (TT_Move_Func)Direct_Move_Orig;

    if (CUR.F_dot_P == 0x40000000L)
    {
        if (CUR.GS.freeVector.x == 0x4000)
        {
            CUR.func_move = (TT_Move_Func)Direct_Move_X;
            CUR.func_move_orig = (TT_Move_Func)Direct_Move_Orig_X;
        }
        else
        {
            if (CUR.GS.freeVector.y == 0x4000)
            {
                CUR.func_move = (TT_Move_Func)Direct_Move_Y;
                CUR.func_move_orig = (TT_Move_Func)Direct_Move_Orig_Y;
            }
        }
    }

    /* at small sizes, F_dot_P can become too small, resulting   */
    /* in overflows and `spikes' in a number of glyphs like `w'. */

    if (FT_ABS(CUR.F_dot_P) < 0x4000000L)
        CUR.F_dot_P = 0x40000000L;

    /* Disable cached aspect ratio */
    CUR.tt_metrics.ratio = 0;
}


/*************************************************************************/
/*                                                                       */
/* <Function>                                                            */
/*    Normalize                                                          */
/*                                                                       */
/* <Description>                                                         */
/*    Norms a vector.                                                    */
/*                                                                       */
/* <Input>                                                               */
/*    Vx :: The horizontal input vector coordinate.                      */
/*    Vy :: The vertical input vector coordinate.                        */
/*                                                                       */
/* <Output>                                                              */
/*    R  :: The normed unit vector.                                      */
/*                                                                       */
/* <Return>                                                              */
/*    Returns FAILURE if a vector parameter is zero.                     */
/*                                                                       */
/* <Note>                                                                */
/*    In case Vx and Vy are both zero, Normalize() returns SUCCESS, and  */
/*    R is undefined.                                                    */
/*                                                                       */


static FT_Bool
Normalize(EXEC_OP_ FT_F26Dot6 Vx,
          FT_F26Dot6 Vy,
          FT_UnitVector* R)
{
    FT_F26Dot6 W;
    FT_Bool S1, S2;

    FT_UNUSED_EXEC;


    if (FT_ABS(Vx) < 0x10000L && FT_ABS(Vy) < 0x10000L)
    {
        Vx *= 0x100;
        Vy *= 0x100;

        W = TT_VecLen(Vx, Vy);

        if (W == 0)
        {
            /* XXX: UNDOCUMENTED! It seems that it is possible to try   */
            /*      to normalize the vector (0,0).  Return immediately. */
            return SUCCESS;
        }

        R->x = (FT_F2Dot14)FT_MulDiv(Vx, 0x4000L, W);
        R->y = (FT_F2Dot14)FT_MulDiv(Vy, 0x4000L, W);

        return SUCCESS;
    }

    W = TT_VecLen(Vx, Vy);

    Vx = FT_MulDiv(Vx, 0x4000L, W);
    Vy = FT_MulDiv(Vy, 0x4000L, W);

    W = Vx * Vx + Vy * Vy;

    /* Now, we want that Sqrt( W ) = 0x4000 */
    /* Or 0x10000000 <= W < 0x10004000        */

    if (Vx < 0)
    {
        Vx = -Vx;
        S1 = TRUE;
    }
    else
        S1 = FALSE;

    if (Vy < 0)
    {
        Vy = -Vy;
        S2 = TRUE;
    }
    else
        S2 = FALSE;

    while (W < 0x10000000L)
    {
        /* We need to increase W by a minimal amount */
        if (Vx < Vy)
            Vx++;
        else
            Vy++;

        W = Vx * Vx + Vy * Vy;
    }

    while (W >= 0x10004000L)
    {
        /* We need to decrease W by a minimal amount */
        if (Vx < Vy)
            Vx--;
        else
            Vy--;

        W = Vx * Vx + Vy * Vy;
    }

    /* Note that in various cases, we can only  */
    /* compute a Sqrt(W) of 0x3FFF, eg. Vx = Vy */

    if (S1)
        Vx = -Vx;

    if (S2)
        Vy = -Vy;

    R->x = (FT_F2Dot14)Vx;   /* Type conversion */
    R->y = (FT_F2Dot14)Vy;   /* Type conversion */

    return SUCCESS;
}


/*************************************************************************/
/*                                                                       */
/* Here we start with the implementation of the various opcodes.         */
/*                                                                       */
/*************************************************************************/


static FT_Bool
Ins_SxVTL(EXEC_OP_ FT_UShort aIdx1,
          FT_UShort aIdx2,
          FT_Int aOpc,
          FT_UnitVector* Vec)
{
    FT_Long A, B, C;
    FT_Vector* p1;
    FT_Vector* p2;


    if (BOUNDS(aIdx1, CUR.zp2.n_points) ||
        BOUNDS(aIdx2, CUR.zp1.n_points))
    {
        if (CUR.pedantic_hinting)
            CUR.error = TT_Err_Invalid_Reference;
        return FAILURE;
    }

    p1 = CUR.zp1.cur + aIdx2;
    p2 = CUR.zp2.cur + aIdx1;

    A = p1->x - p2->x;
    B = p1->y - p2->y;

    if ((aOpc & 1) != 0)
    {
        C = B;  /* counter clockwise rotation */
        B = A;
        A = -C;
    }

    NORMalize(A, B, Vec);

    return SUCCESS;
}


/* When not using the big switch statements, the interpreter uses a */
/* call table defined later below in this source.  Each opcode must */
/* thus have a corresponding function, even trivial ones.           */
/*                                                                  */
/* They are all defined there.                                      */

    #define DO_SVTCA \
    { \
        FT_Short A, B; \
 \
 \
        A = (FT_Short)(CUR.opcode & 1) << 14; \
        B = A ^ (FT_Short)0x4000; \
 \
        CUR.GS.freeVector.x = A; \
        CUR.GS.projVector.x = A; \
        CUR.GS.dualVector.x = A; \
 \
        CUR.GS.freeVector.y = B; \
        CUR.GS.projVector.y = B; \
        CUR.GS.dualVector.y = B; \
 \
        COMPUTE_Funcs(); \
    }


    #define DO_SPVTCA \
    { \
        FT_Short A, B; \
 \
 \
        A = (FT_Short)(CUR.opcode & 1) << 14; \
        B = A ^ (FT_Short)0x4000; \
 \
        CUR.GS.projVector.x = A; \
        CUR.GS.dualVector.x = A; \
 \
        CUR.GS.projVector.y = B; \
        CUR.GS.dualVector.y = B; \
 \
        GUESS_VECTOR(freeVector); \
 \
        COMPUTE_Funcs(); \
    }


    #define DO_SFVTCA \
    { \
        FT_Short A, B; \
 \
 \
        A = (FT_Short)(CUR.opcode & 1) << 14; \
        B = A ^ (FT_Short)0x4000; \
 \
        CUR.GS.freeVector.x = A; \
        CUR.GS.freeVector.y = B; \
 \
        GUESS_VECTOR(projVector); \
 \
        COMPUTE_Funcs(); \
    }


    #define DO_SPVTL \
    if (INS_SxVTL((FT_UShort)args[1], \
                  (FT_UShort)args[0], \
                  CUR.opcode, \
                  &CUR.GS.projVector) == SUCCESS) \
    { \
        CUR.GS.dualVector = CUR.GS.projVector; \
        GUESS_VECTOR(freeVector); \
        COMPUTE_Funcs(); \
    }


    #define DO_SFVTL \
    if (INS_SxVTL((FT_UShort)args[1], \
                  (FT_UShort)args[0], \
                  CUR.opcode, \
                  &CUR.GS.freeVector) == SUCCESS) \
    { \
        GUESS_VECTOR(projVector); \
        COMPUTE_Funcs(); \
    }


    #define DO_SFVTPV \
    GUESS_VECTOR(projVector); \
    CUR.GS.freeVector = CUR.GS.projVector; \
    COMPUTE_Funcs();


    #define DO_SPVFS \
    { \
        FT_Short S; \
        FT_Long X, Y; \
 \
 \
        /* Only use low 16bits, then sign extend */ \
        S = (FT_Short)args[1]; \
        Y = (FT_Long)S; \
        S = (FT_Short)args[0]; \
        X = (FT_Long)S; \
 \
        NORMalize(X, Y, &CUR.GS.projVector); \
 \
        CUR.GS.dualVector = CUR.GS.projVector; \
        GUESS_VECTOR(freeVector); \
        COMPUTE_Funcs(); \
    }


    #define DO_SFVFS \
    { \
        FT_Short S; \
        FT_Long X, Y; \
 \
 \
        /* Only use low 16bits, then sign extend */ \
        S = (FT_Short)args[1]; \
        Y = (FT_Long)S; \
        S = (FT_Short)args[0]; \
        X = S; \
 \
        NORMalize(X, Y, &CUR.GS.freeVector); \
        GUESS_VECTOR(projVector); \
        COMPUTE_Funcs(); \
    }


    #ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
        #define DO_GPV \
    if (CUR.face->unpatented_hinting) \
    { \
        args[0] = CUR.GS.both_x_axis ? 0x4000 : 0; \
        args[1] = CUR.GS.both_x_axis ? 0 : 0x4000; \
    } \
    else \
    { \
        args[0] = CUR.GS.projVector.x; \
        args[1] = CUR.GS.projVector.y; \
    }
    #else
        #define DO_GPV \
    args[0] = CUR.GS.projVector.x; \
    args[1] = CUR.GS.projVector.y;
    #endif


    #ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
        #define DO_GFV \
    if (CUR.face->unpatented_hinting) \
    { \
        args[0] = CUR.GS.both_x_axis ? 0x4000 : 0; \
        args[1] = CUR.GS.both_x_axis ? 0 : 0x4000; \
    } \
    else \
    { \
        args[0] = CUR.GS.freeVector.x; \
        args[1] = CUR.GS.freeVector.y; \
    }
    #else
        #define DO_GFV \
    args[0] = CUR.GS.freeVector.x; \
    args[1] = CUR.GS.freeVector.y;
    #endif


    #define DO_SRP0 \
    CUR.GS.rp0 = (FT_UShort)args[0];


    #define DO_SRP1 \
    CUR.GS.rp1 = (FT_UShort)args[0];


    #define DO_SRP2 \
    CUR.GS.rp2 = (FT_UShort)args[0];


    #define DO_RTHG \
    CUR.GS.round_state = TT_Round_To_Half_Grid; \
    CUR.func_round = (TT_Round_Func)Round_To_Half_Grid;


    #define DO_RTG \
    CUR.GS.round_state = TT_Round_To_Grid; \
    CUR.func_round = (TT_Round_Func)Round_To_Grid;


    #define DO_RTDG \
    CUR.GS.round_state = TT_Round_To_Double_Grid; \
    CUR.func_round = (TT_Round_Func)Round_To_Double_Grid;


    #define DO_RUTG \
    CUR.GS.round_state = TT_Round_Up_To_Grid; \
    CUR.func_round = (TT_Round_Func)Round_Up_To_Grid;


    #define DO_RDTG \
    CUR.GS.round_state = TT_Round_Down_To_Grid; \
    CUR.func_round = (TT_Round_Func)Round_Down_To_Grid;


    #define DO_ROFF \
    CUR.GS.round_state = TT_Round_Off; \
    CUR.func_round = (TT_Round_Func)Round_None;


    #define DO_SROUND \
    SET_SuperRound(0x4000, args[0]); \
    CUR.GS.round_state = TT_Round_Super; \
    CUR.func_round = (TT_Round_Func)Round_Super;


    #define DO_S45ROUND \
    SET_SuperRound(0x2D41, args[0]); \
    CUR.GS.round_state = TT_Round_Super_45; \
    CUR.func_round = (TT_Round_Func)Round_Super_45;


    #define DO_SLOOP \
    if (args[0] < 0) \
        CUR.error = TT_Err_Bad_Argument; \
    else \
        CUR.GS.loop = args[0];


    #define DO_SMD \
    CUR.GS.minimum_distance = args[0];


    #define DO_SCVTCI \
    CUR.GS.control_value_cutin = (FT_F26Dot6)args[0];


    #define DO_SSWCI \
    CUR.GS.single_width_cutin = (FT_F26Dot6)args[0];


/* XXX: UNDOCUMENTED! or bug in the Windows engine?   */
/*                                                    */
/*      It seems that the value that is read here is  */
/*      expressed in 16.16 format rather than in font */
/*      units.                                        */
/*                                                    */
    #define DO_SSW \
    CUR.GS.single_width_value = (FT_F26Dot6)(args[0] >> 10);


    #define DO_FLIPON \
    CUR.GS.auto_flip = TRUE;


    #define DO_FLIPOFF \
    CUR.GS.auto_flip = FALSE;


    #define DO_SDB \
    CUR.GS.delta_base = (FT_Short)args[0];


    #define DO_SDS \
    CUR.GS.delta_shift = (FT_Short)args[0];


    #define DO_MD /* nothing */


    #define DO_MPPEM \
    args[0] = CURRENT_Ppem();


/* Note: The pointSize should be irrelevant in a given font program; */
/*       we thus decide to return only the ppem.                     */
    #if 0

        #define DO_MPS \
    args[0] = CUR.metrics.pointSize;

    #else

        #define DO_MPS \
    args[0] = CURRENT_Ppem();

    #endif /* 0 */


    #define DO_DUP \
    args[1] = args[0];


    #define DO_CLEAR \
    CUR.new_top = 0;


    #define DO_SWAP \
    { \
        FT_Long L; \
 \
 \
        L = args[0]; \
        args[0] = args[1]; \
        args[1] = L; \
    }


    #define DO_DEPTH \
    args[0] = CUR.top;


    #define DO_CINDEX \
    { \
        FT_Long L; \
 \
 \
        L = args[0]; \
 \
        if (L <= 0 || L > CUR.args) \
            CUR.error = TT_Err_Invalid_Reference; \
        else \
            args[0] = CUR.stack[CUR.args - L]; \
    }


    #define DO_JROT \
    if (args[1] != 0) \
    { \
        CUR.IP += args[0]; \
        CUR.step_ins = FALSE; \
    }


    #define DO_JMPR \
    CUR.IP += args[0]; \
    CUR.step_ins = FALSE;


    #define DO_JROF \
    if (args[1] == 0) \
    { \
        CUR.IP += args[0]; \
        CUR.step_ins = FALSE; \
    }


    #define DO_LT \
    args[0] = (args[0] < args[1]);


    #define DO_LTEQ \
    args[0] = (args[0] <= args[1]);


    #define DO_GT \
    args[0] = (args[0] > args[1]);


    #define DO_GTEQ \
    args[0] = (args[0] >= args[1]);


    #define DO_EQ \
    args[0] = (args[0] == args[1]);


    #define DO_NEQ \
    args[0] = (args[0] != args[1]);


    #define DO_ODD \
    args[0] = ((CUR_Func_round(args[0], 0) & 127) == 64);


    #define DO_EVEN \
    args[0] = ((CUR_Func_round(args[0], 0) & 127) == 0);


    #define DO_AND \
    args[0] = (args[0] && args[1]);


    #define DO_OR \
    args[0] = (args[0] || args[1]);


    #define DO_NOT \
    args[0] = !args[0];


    #define DO_ADD \
    args[0] += args[1];


    #define DO_SUB \
    args[0] -= args[1];


    #define DO_DIV \
    if (args[1] == 0) \
        CUR.error = TT_Err_Divide_By_Zero; \
    else \
        args[0] = TT_MULDIV_NO_ROUND(args[0], 64L, args[1]);


    #define DO_MUL \
    args[0] = TT_MULDIV(args[0], args[1], 64L);


    #define DO_ABS \
    args[0] = FT_ABS(args[0]);


    #define DO_NEG \
    args[0] = -args[0];


    #define DO_FLOOR \
    args[0] = FT_PIX_FLOOR(args[0]);


    #define DO_CEILING \
    args[0] = FT_PIX_CEIL(args[0]);


    #define DO_RS \
    { \
        FT_ULong I = (FT_ULong)args[0]; \
 \
 \
        if (BOUNDS(I, CUR.storeSize)) \
        { \
            if (CUR.pedantic_hinting) \
            { \
                ARRAY_BOUND_ERROR; \
            } \
            else \
                args[0] = 0; \
        } \
        else \
            args[0] = CUR.storage[I]; \
    }


    #define DO_WS \
    { \
        FT_ULong I = (FT_ULong)args[0]; \
 \
 \
        if (BOUNDS(I, CUR.storeSize)) \
        { \
            if (CUR.pedantic_hinting) \
            { \
                ARRAY_BOUND_ERROR; \
            } \
        } \
        else \
            CUR.storage[I] = args[1]; \
    }


    #define DO_RCVT \
    { \
        FT_ULong I = (FT_ULong)args[0]; \
 \
 \
        if (BOUNDS(I, CUR.cvtSize)) \
        { \
            if (CUR.pedantic_hinting) \
            { \
                ARRAY_BOUND_ERROR; \
            } \
            else \
                args[0] = 0; \
        } \
        else \
            args[0] = CUR_Func_read_cvt(I); \
    }


    #define DO_WCVTP \
    { \
        FT_ULong I = (FT_ULong)args[0]; \
 \
 \
        if (BOUNDS(I, CUR.cvtSize)) \
        { \
            if (CUR.pedantic_hinting) \
            { \
                ARRAY_BOUND_ERROR; \
            } \
        } \
        else \
            CUR_Func_write_cvt(I, args[1]); \
    }


    #define DO_WCVTF \
    { \
        FT_ULong I = (FT_ULong)args[0]; \
 \
 \
        if (BOUNDS(I, CUR.cvtSize)) \
        { \
            if (CUR.pedantic_hinting) \
            { \
                ARRAY_BOUND_ERROR; \
            } \
        } \
        else \
            CUR.cvt[I] = TT_MULFIX(args[1], CUR.tt_metrics.scale); \
    }


    #define DO_DEBUG \
    CUR.error = TT_Err_Debug_OpCode;


    #define DO_ROUND \
    args[0] = CUR_Func_round( \
        args[0], \
        CUR.tt_metrics.compensations[CUR.opcode - 0x68]);


    #define DO_NROUND \
    args[0] = ROUND_None(args[0], \
                         CUR.tt_metrics.compensations[CUR.opcode - 0x6C]);


    #define DO_MAX \
    if (args[1] > args[0]) \
        args[0] = args[1];


    #define DO_MIN \
    if (args[1] < args[0]) \
        args[0] = args[1];


    #ifndef TT_CONFIG_OPTION_INTERPRETER_SWITCH


        #undef  ARRAY_BOUND_ERROR
        #define ARRAY_BOUND_ERROR \
    { \
        CUR.error = TT_Err_Invalid_Reference; \
        return; \
    }


/*************************************************************************/
/*                                                                       */
/* SVTCA[a]:     Set (F and P) Vectors to Coordinate Axis                */
/* Opcode range: 0x00-0x01                                               */
/* Stack:        -->                                                     */
/*                                                                       */
static void
Ins_SVTCA(INS_ARG)
{
    DO_SVTCA
}


/*************************************************************************/
/*                                                                       */
/* SPVTCA[a]:    Set PVector to Coordinate Axis                          */
/* Opcode range: 0x02-0x03                                               */
/* Stack:        -->                                                     */
/*                                                                       */
static void
Ins_SPVTCA(INS_ARG)
{
    DO_SPVTCA
}


/*************************************************************************/
/*                                                                       */
/* SFVTCA[a]:    Set FVector to Coordinate Axis                          */
/* Opcode range: 0x04-0x05                                               */
/* Stack:        -->                                                     */
/*                                                                       */
static void
Ins_SFVTCA(INS_ARG)
{
    DO_SFVTCA
}


/*************************************************************************/
/*                                                                       */
/* SPVTL[a]:     Set PVector To Line                                     */
/* Opcode range: 0x06-0x07                                               */
/* Stack:        uint32 uint32 -->                                       */
/*                                                                       */
static void
Ins_SPVTL(INS_ARG)
{
    DO_SPVTL
}


/*************************************************************************/
/*                                                                       */
/* SFVTL[a]:     Set FVector To Line                                     */
/* Opcode range: 0x08-0x09                                               */
/* Stack:        uint32 uint32 -->                                       */
/*                                                                       */
static void
Ins_SFVTL(INS_ARG)
{
    DO_SFVTL
}


/*************************************************************************/
/*                                                                       */
/* SFVTPV[]:     Set FVector To PVector                                  */
/* Opcode range: 0x0E                                                    */
/* Stack:        -->                                                     */
/*                                                                       */
static void
Ins_SFVTPV(INS_ARG)
{
    DO_SFVTPV
}


/*************************************************************************/
/*                                                                       */
/* SPVFS[]:      Set PVector From Stack                                  */
/* Opcode range: 0x0A                                                    */
/* Stack:        f2.14 f2.14 -->                                         */
/*                                                                       */
static void
Ins_SPVFS(INS_ARG)
{
    DO_SPVFS
}


/*************************************************************************/
/*                                                                       */
/* SFVFS[]:      Set FVector From Stack                                  */
/* Opcode range: 0x0B                                                    */
/* Stack:        f2.14 f2.14 -->                                         */
/*                                                                       */
static void
Ins_SFVFS(INS_ARG)
{
    DO_SFVFS
}


/*************************************************************************/
/*                                                                       */
/* GPV[]:        Get Projection Vector                                   */
/* Opcode range: 0x0C                                                    */
/* Stack:        ef2.14 --> ef2.14                                       */
/*                                                                       */
static void
Ins_GPV(INS_ARG)
{
    DO_GPV
}


/*************************************************************************/
/* GFV[]:        Get Freedom Vector                                      */
/* Opcode range: 0x0D                                                    */
/* Stack:        ef2.14 --> ef2.14                                       */
/*                                                                       */
static void
Ins_GFV(INS_ARG)
{
    DO_GFV
}


/*************************************************************************/
/*                                                                       */
/* SRP0[]:       Set Reference Point 0                                   */
/* Opcode range: 0x10                                                    */
/* Stack:        uint32 -->                                              */
/*                                                                       */
static void
Ins_SRP0(INS_ARG)
{
    DO_SRP0
}


/*************************************************************************/
/*                                                                       */
/* SRP1[]:       Set Reference Point 1                                   */
/* Opcode range: 0x11                                                    */
/* Stack:        uint32 -->                                              */
/*                                                                       */
static void
Ins_SRP1(INS_ARG)
{
    DO_SRP1
}


/*************************************************************************/
/*                                                                       */
/* SRP2[]:       Set Reference Point 2                                   */
/* Opcode range: 0x12                                                    */
/* Stack:        uint32 -->                                              */
/*                                                                       */
static void
Ins_SRP2(INS_ARG)
{
    DO_SRP2
}


/*************************************************************************/
/*                                                                       */
/* RTHG[]:       Round To Half Grid                                      */
/* Opcode range: 0x19                                                    */
/* Stack:        -->                                                     */
/*                                                                       */
static void
Ins_RTHG(INS_ARG)
{
    DO_RTHG
}


/*************************************************************************/
/*                                                                       */
/* RTG[]:        Round To Grid                                           */
/* Opcode range: 0x18                                                    */
/* Stack:        -->                                                     */
/*                                                                       */
static void
Ins_RTG(INS_ARG)
{
    DO_RTG
}


/*************************************************************************/
/* RTDG[]:       Round To Double Grid                                    */
/* Opcode range: 0x3D                                                    */
/* Stack:        -->                                                     */
/*                                                                       */
static void
Ins_RTDG(INS_ARG)
{
    DO_RTDG
}


/*************************************************************************/
/* RUTG[]:       Round Up To Grid                                        */
/* Opcode range: 0x7C                                                    */
/* Stack:        -->                                                     */
/*                                                                       */
static void
Ins_RUTG(INS_ARG)
{
    DO_RUTG
}


/*************************************************************************/
/*                                                                       */
/* RDTG[]:       Round Down To Grid                                      */
/* Opcode range: 0x7D                                                    */
/* Stack:        -->                                                     */
/*                                                                       */
static void
Ins_RDTG(INS_ARG)
{
    DO_RDTG
}


/*************************************************************************/
/*                                                                       */
/* ROFF[]:       Round OFF                                               */
/* Opcode range: 0x7A                                                    */
/* Stack:        -->                                                     */
/*                                                                       */
static void
Ins_ROFF(INS_ARG)
{
    DO_ROFF
}


/*************************************************************************/
/*                                                                       */
/* SROUND[]:     Super ROUND                                             */
/* Opcode range: 0x76                                                    */
/* Stack:        Eint8 -->                                               */
/*                                                                       */
static void
Ins_SROUND(INS_ARG)
{
    DO_SROUND
}


/*************************************************************************/
/*                                                                       */
/* S45ROUND[]:   Super ROUND 45 degrees                                  */
/* Opcode range: 0x77                                                    */
/* Stack:        uint32 -->                                              */
/*                                                                       */
static void
Ins_S45ROUND(INS_ARG)
{
    DO_S45ROUND
}


/*************************************************************************/
/*                                                                       */
/* SLOOP[]:      Set LOOP variable                                       */
/* Opcode range: 0x17                                                    */
/* Stack:        int32? -->                                              */
/*                                                                       */
static void
Ins_SLOOP(INS_ARG)
{
    DO_SLOOP
}


/*************************************************************************/
/*                                                                       */
/* SMD[]:        Set Minimum Distance                                    */
/* Opcode range: 0x1A                                                    */
/* Stack:        f26.6 -->                                               */
/*                                                                       */
static void
Ins_SMD(INS_ARG)
{
    DO_SMD
}


/*************************************************************************/
/*                                                                       */
/* SCVTCI[]:     Set Control Value Table Cut In                          */
/* Opcode range: 0x1D                                                    */
/* Stack:        f26.6 -->                                               */
/*                                                                       */
static void
Ins_SCVTCI(INS_ARG)
{
    DO_SCVTCI
}


/*************************************************************************/
/*                                                                       */
/* SSWCI[]:      Set Single Width Cut In                                 */
/* Opcode range: 0x1E                                                    */
/* Stack:        f26.6 -->                                               */
/*                                                                       */
static void
Ins_SSWCI(INS_ARG)
{
    DO_SSWCI
}


/*************************************************************************/
/*                                                                       */
/* SSW[]:        Set Single Width                                        */
/* Opcode range: 0x1F                                                    */
/* Stack:        int32? -->                                              */
/*                                                                       */
static void
Ins_SSW(INS_ARG)
{
    DO_SSW
}


/*************************************************************************/
/*                                                                       */
/* FLIPON[]:     Set auto-FLIP to ON                                     */
/* Opcode range: 0x4D                                                    */
/* Stack:        -->                                                     */
/*                                                                       */
static void
Ins_FLIPON(INS_ARG)
{
    DO_FLIPON
}


/*************************************************************************/
/*                                                                       */
/* FLIPOFF[]:    Set auto-FLIP to OFF                                    */
/* Opcode range: 0x4E                                                    */
/* Stack: -->                                                            */
/*                                                                       */
static void
Ins_FLIPOFF(INS_ARG)
{
    DO_FLIPOFF
}


/*************************************************************************/
/*                                                                       */
/* SANGW[]:      Set ANGle Weight                                        */
/* Opcode range: 0x7E                                                    */
/* Stack:        uint32 -->                                              */
/*                                                                       */
static void
Ins_SANGW(INS_ARG)
{
    /* instruction not supported anymore */
}


/*************************************************************************/
/*                                                                       */
/* SDB[]:        Set Delta Base                                          */
/* Opcode range: 0x5E                                                    */
/* Stack:        uint32 -->                                              */
/*                                                                       */
static void
Ins_SDB(INS_ARG)
{
    DO_SDB
}


/*************************************************************************/
/*                                                                       */
/* SDS[]:        Set Delta Shift                                         */
/* Opcode range: 0x5F                                                    */
/* Stack:        uint32 -->                                              */
/*                                                                       */
static void
Ins_SDS(INS_ARG)
{
    DO_SDS
}


/*************************************************************************/
/*                                                                       */
/* MPPEM[]:      Measure Pixel Per EM                                    */
/* Opcode range: 0x4B                                                    */
/* Stack:        --> Euint16                                             */
/*                                                                       */
static void
Ins_MPPEM(INS_ARG)
{
    DO_MPPEM
}


/*************************************************************************/
/*                                                                       */
/* MPS[]:        Measure Point Size                                      */
/* Opcode range: 0x4C                                                    */
/* Stack:        --> Euint16                                             */
/*                                                                       */
static void
Ins_MPS(INS_ARG)
{
    DO_MPS
}


/*************************************************************************/
/*                                                                       */
/* DUP[]:        DUPlicate the top stack's element                       */
/* Opcode range: 0x20                                                    */
/* Stack:        StkElt --> StkElt StkElt                                */
/*                                                                       */
static void
Ins_DUP(INS_ARG)
{
    DO_DUP
}


/*************************************************************************/
/*                                                                       */
/* POP[]:        POP the stack's top element                             */
/* Opcode range: 0x21                                                    */
/* Stack:        StkElt -->                                              */
/*                                                                       */
static void
Ins_POP(INS_ARG)
{
    /* nothing to do */
}


/*************************************************************************/
/*                                                                       */
/* CLEAR[]:      CLEAR the entire stack                                  */
/* Opcode range: 0x22                                                    */
/* Stack:        StkElt... -->                                           */
/*                                                                       */
static void
Ins_CLEAR(INS_ARG)
{
    DO_CLEAR
}


/*************************************************************************/
/*                                                                       */
/* SWAP[]:       SWAP the stack's top two elements                       */
/* Opcode range: 0x23                                                    */
/* Stack:        2 * StkElt --> 2 * StkElt                               */
/*                                                                       */
static void
Ins_SWAP(INS_ARG)
{
    DO_SWAP
}


/*************************************************************************/
/*                                                                       */
/* DEPTH[]:      return the stack DEPTH                                  */
/* Opcode range: 0x24                                                    */
/* Stack:        --> uint32                                              */
/*                                                                       */
static void
Ins_DEPTH(INS_ARG)
{
    DO_DEPTH
}


/*************************************************************************/
/*                                                                       */
/* CINDEX[]:     Copy INDEXed element                                    */
/* Opcode range: 0x25                                                    */
/* Stack:        int32 --> StkElt                                        */
/*                                                                       */
static void
Ins_CINDEX(INS_ARG)
{
    DO_CINDEX
}


/*************************************************************************/
/*                                                                       */
/* EIF[]:        End IF                                                  */
/* Opcode range: 0x59                                                    */
/* Stack:        -->                                                     */
/*                                                                       */
static void
Ins_EIF(INS_ARG)
{
    /* nothing to do */
}


/*************************************************************************/
/*                                                                       */
/* JROT[]:       Jump Relative On True                                   */
/* Opcode range: 0x78                                                    */
/* Stack:        StkElt int32 -->                                        */
/*                                                                       */
static void
Ins_JROT(INS_ARG)
{
    DO_JROT
}


/*************************************************************************/
/*                                                                       */
/* JMPR[]:       JuMP Relative                                           */
/* Opcode range: 0x1C                                                    */
/* Stack:        int32 -->                                               */
/*                                                                       */
static void
Ins_JMPR(INS_ARG)
{
    DO_JMPR
}


/*************************************************************************/
/*                                                                       */
/* JROF[]:       Jump Relative On False                                  */
/* Opcode range: 0x79                                                    */
/* Stack:        StkElt int32 -->                                        */
/*                                                                       */
static void
Ins_JROF(INS_ARG)
{
    DO_JROF
}


/*************************************************************************/
/*                                                                       */
/* LT[]:         Less Than                                               */
/* Opcode range: 0x50                                                    */
/* Stack:        int32? int32? --> bool                                  */
/*                                                                       */
static void
Ins_LT(INS_ARG)
{
    DO_LT
}


/*************************************************************************/
/*                                                                       */
/* LTEQ[]:       Less Than or EQual                                      */
/* Opcode range: 0x51                                                    */
/* Stack:        int32? int32? --> bool                                  */
/*                                                                       */
static void
Ins_LTEQ(INS_ARG)
{
    DO_LTEQ
}


/*************************************************************************/
/*                                                                       */
/* GT[]:         Greater Than                                            */
/* Opcode range: 0x52                                                    */
/* Stack:        int32? int32? --> bool                                  */
/*                                                                       */
static void
Ins_GT(INS_ARG)
{
    DO_GT
}


/*************************************************************************/
/*                                                                       */
/* GTEQ[]:       Greater Than or EQual                                   */
/* Opcode range: 0x53                                                    */
/* Stack:        int32? int32? --> bool                                  */
/*                                                                       */
static void
Ins_GTEQ(INS_ARG)
{
    DO_GTEQ
}


/*************************************************************************/
/*                                                                       */
/* EQ[]:         EQual                                                   */
/* Opcode range: 0x54                                                    */
/* Stack:        StkElt StkElt --> bool                                  */
/*                                                                       */
static void
Ins_EQ(INS_ARG)
{
    DO_EQ
}


/*************************************************************************/
/*                                                                       */
/* NEQ[]:        Not EQual                                               */
/* Opcode range: 0x55                                                    */
/* Stack:        StkElt StkElt --> bool                                  */
/*                                                                       */
static void
Ins_NEQ(INS_ARG)
{
    DO_NEQ
}


/*************************************************************************/
/*                                                                       */
/* ODD[]:        Is ODD                                                  */
/* Opcode range: 0x56                                                    */
/* Stack:        f26.6 --> bool                                          */
/*                                                                       */
static void
Ins_ODD(INS_ARG)
{
    DO_ODD
}


/*************************************************************************/
/*                                                                       */
/* EVEN[]:       Is EVEN                                                 */
/* Opcode range: 0x57                                                    */
/* Stack:        f26.6 --> bool                                          */
/*                                                                       */
static void
Ins_EVEN(INS_ARG)
{
    DO_EVEN
}


/*************************************************************************/
/*                                                                       */
/* AND[]:        logical AND                                             */
/* Opcode range: 0x5A                                                    */
/* Stack:        uint32 uint32 --> uint32                                */
/*                                                                       */
static void
Ins_AND(INS_ARG)
{
    DO_AND
}


/*************************************************************************/
/*                                                                       */
/* OR[]:         logical OR                                              */
/* Opcode range: 0x5B                                                    */
/* Stack:        uint32 uint32 --> uint32                                */
/*                                                                       */
static void
Ins_OR(INS_ARG)
{
    DO_OR
}


/*************************************************************************/
/*                                                                       */
/* NOT[]:        logical NOT                                             */
/* Opcode range: 0x5C                                                    */
/* Stack:        StkElt --> uint32                                       */
/*                                                                       */
static void
Ins_NOT(INS_ARG)
{
    DO_NOT
}


/*************************************************************************/
/*                                                                       */
/* ADD[]:        ADD                                                     */
/* Opcode range: 0x60                                                    */
/* Stack:        f26.6 f26.6 --> f26.6                                   */
/*                                                                       */
static void
Ins_ADD(INS_ARG)
{
    DO_ADD
}


/*************************************************************************/
/*                                                                       */
/* SUB[]:        SUBtract                                                */
/* Opcode range: 0x61                                                    */
/* Stack:        f26.6 f26.6 --> f26.6                                   */
/*                                                                       */
static void
Ins_SUB(INS_ARG)
{
    DO_SUB
}


/*************************************************************************/
/*                                                                       */
/* DIV[]:        DIVide                                                  */
/* Opcode range: 0x62                                                    */
/* Stack:        f26.6 f26.6 --> f26.6                                   */
/*                                                                       */
static void
Ins_DIV(INS_ARG)
{
    DO_DIV
}


/*************************************************************************/
/*                                                                       */
/* MUL[]:        MULtiply                                                */
/* Opcode range: 0x63                                                    */
/* Stack:        f26.6 f26.6 --> f26.6                                   */
/*                                                                       */
static void
Ins_MUL(INS_ARG)
{
    DO_MUL
}


/*************************************************************************/
/*                                                                       */
/* ABS[]:        ABSolute value                                          */
/* Opcode range: 0x64                                                    */
/* Stack:        f26.6 --> f26.6                                         */
/*                                                                       */
static void
Ins_ABS(INS_ARG)
{
    DO_ABS
}


/*************************************************************************/
/*                                                                       */
/* NEG[]:        NEGate                                                  */
/* Opcode range: 0x65                                                    */
/* Stack: f26.6 --> f26.6                                                */
/*                                                                       */
static void
Ins_NEG(INS_ARG)
{
    DO_NEG
}


/*************************************************************************/
/*                                                                       */
/* FLOOR[]:      FLOOR                                                   */
/* Opcode range: 0x66                                                    */
/* Stack:        f26.6 --> f26.6                                         */
/*                                                                       */
static void
Ins_FLOOR(INS_ARG)
{
    DO_FLOOR
}


/*************************************************************************/
/*                                                                       */
/* CEILING[]:    CEILING                                                 */
/* Opcode range: 0x67                                                    */
/* Stack:        f26.6 --> f26.6                                         */
/*                                                                       */
static void
Ins_CEILING(INS_ARG)
{
    DO_CEILING
}


/*************************************************************************/
/*                                                                       */
/* RS[]:         Read Store                                              */
/* Opcode range: 0x43                                                    */
/* Stack:        uint32 --> uint32                                       */
/*                                                                       */
static void
Ins_RS(INS_ARG)
{
    DO_RS
}


/*************************************************************************/
/*                                                                       */
/* WS[]:         Write Store                                             */
/* Opcode range: 0x42                                                    */
/* Stack:        uint32 uint32 -->                                       */
/*                                                                       */
static void
Ins_WS(INS_ARG)
{
    DO_WS
}


/*************************************************************************/
/*                                                                       */
/* WCVTP[]:      Write CVT in Pixel units                                */
/* Opcode range: 0x44                                                    */
/* Stack:        f26.6 uint32 -->                                        */
/*                                                                       */
static void
Ins_WCVTP(INS_ARG)
{
    DO_WCVTP
}


/*************************************************************************/
/*                                                                       */
/* WCVTF[]:      Write CVT in Funits                                     */
/* Opcode range: 0x70                                                    */
/* Stack:        uint32 uint32 -->                                       */
/*                                                                       */
static void
Ins_WCVTF(INS_ARG)
{
    DO_WCVTF
}


/*************************************************************************/
/*                                                                       */
/* RCVT[]:       Read CVT                                                */
/* Opcode range: 0x45                                                    */
/* Stack:        uint32 --> f26.6                                        */
/*                                                                       */
static void
Ins_RCVT(INS_ARG)
{
    DO_RCVT
}


/*************************************************************************/
/*                                                                       */
/* AA[]:         Adjust Angle                                            */
/* Opcode range: 0x7F                                                    */
/* Stack:        uint32 -->                                              */
/*                                                                       */
static void
Ins_AA(INS_ARG)
{
    /* intentionally no longer supported */
}


/*************************************************************************/
/*                                                                       */
/* DEBUG[]:      DEBUG.  Unsupported.                                    */
/* Opcode range: 0x4F                                                    */
/* Stack:        uint32 -->                                              */
/*                                                                       */
/* Note: The original instruction pops a value from the stack.           */
/*                                                                       */
static void
Ins_DEBUG(INS_ARG)
{
    DO_DEBUG
}


/*************************************************************************/
/*                                                                       */
/* ROUND[ab]:    ROUND value                                             */
/* Opcode range: 0x68-0x6B                                               */
/* Stack:        f26.6 --> f26.6                                         */
/*                                                                       */
static void
Ins_ROUND(INS_ARG)
{
    DO_ROUND
}


/*************************************************************************/
/*                                                                       */
/* NROUND[ab]:   No ROUNDing of value                                    */
/* Opcode range: 0x6C-0x6F                                               */
/* Stack:        f26.6 --> f26.6                                         */
/*                                                                       */
static void
Ins_NROUND(INS_ARG)
{
    DO_NROUND
}


/*************************************************************************/
/*                                                                       */
/* MAX[]:        MAXimum                                                 */
/* Opcode range: 0x68                                                    */
/* Stack:        int32? int32? --> int32                                 */
/*                                                                       */
static void
Ins_MAX(INS_ARG)
{
    DO_MAX
}


/*************************************************************************/
/*                                                                       */
/* MIN[]:        MINimum                                                 */
/* Opcode range: 0x69                                                    */
/* Stack:        int32? int32? --> int32                                 */
/*                                                                       */
static void
Ins_MIN(INS_ARG)
{
    DO_MIN
}


    #endif /* !TT_CONFIG_OPTION_INTERPRETER_SWITCH */


/*************************************************************************/
/*                                                                       */
/* The following functions are called as is within the switch statement. */
/*                                                                       */
/*************************************************************************/


/*************************************************************************/
/*                                                                       */
/* MINDEX[]:     Move INDEXed element                                    */
/* Opcode range: 0x26                                                    */
/* Stack:        int32? --> StkElt                                       */
/*                                                                       */
static void
Ins_MINDEX(INS_ARG)
{
    FT_Long L, K;


    L = args[0];

    if (L <= 0 || L > CUR.args)
    {
        CUR.error = TT_Err_Invalid_Reference;
        return;
    }

    K = CUR.stack[CUR.args - L];

    FT_ARRAY_MOVE(&CUR.stack[CUR.args - L],
                  &CUR.stack[CUR.args - L + 1],
                  (L - 1));

    CUR.stack[CUR.args - 1] = K;
}


/*************************************************************************/
/*                                                                       */
/* ROLL[]:       ROLL top three elements                                 */
/* Opcode range: 0x8A                                                    */
/* Stack:        3 * StkElt --> 3 * StkElt                               */
/*                                                                       */
static void
Ins_ROLL(INS_ARG)
{
    FT_Long A, B, C;

    FT_UNUSED_EXEC;


    A = args[2];
    B = args[1];
    C = args[0];

    args[2] = C;
    args[1] = A;
    args[0] = B;
}


/*************************************************************************/
/*                                                                       */
/* MANAGING THE FLOW OF CONTROL                                          */
/*                                                                       */
/*   Instructions appear in the specification's order.                   */
/*                                                                       */
/*************************************************************************/


static FT_Bool
SkipCode(EXEC_OP)
{
    CUR.IP += CUR.length;

    if (CUR.IP < CUR.codeSize)
    {
        CUR.opcode = CUR.code[CUR.IP];

        CUR.length = opcode_length[CUR.opcode];
        if (CUR.length < 0)
        {
            if (CUR.IP + 1 > CUR.codeSize)
                goto Fail_Overflow;
            CUR.length = 2 - CUR.length * CUR.code[CUR.IP + 1];
        }

        if (CUR.IP + CUR.length <= CUR.codeSize)
            return SUCCESS;
    }

Fail_Overflow:
    CUR.error = TT_Err_Code_Overflow;
    return FAILURE;
}


/*************************************************************************/
/*                                                                       */
/* IF[]:         IF test                                                 */
/* Opcode range: 0x58                                                    */
/* Stack:        StkElt -->                                              */
/*                                                                       */
static void
Ins_IF(INS_ARG)
{
    FT_Int nIfs;
    FT_Bool Out;


    if (args[0] != 0)
        return;

    nIfs = 1;
    Out = 0;

    do
    {
        if (SKIP_Code() == FAILURE)
            return;

        switch (CUR.opcode)
        {
            case 0x58: /* IF */
                nIfs++;
                break;

            case 0x1B: /* ELSE */
                Out = FT_BOOL(nIfs == 1);
                break;

            case 0x59: /* EIF */
                nIfs--;
                Out = FT_BOOL(nIfs == 0);
                break;
        }
    }
    while (Out == 0);
}


/*************************************************************************/
/*                                                                       */
/* ELSE[]:       ELSE                                                    */
/* Opcode range: 0x1B                                                    */
/* Stack:        -->                                                     */
/*                                                                       */
static void
Ins_ELSE(INS_ARG)
{
    FT_Int nIfs;

    FT_UNUSED_ARG;


    nIfs = 1;

    do
    {
        if (SKIP_Code() == FAILURE)
            return;

        switch (CUR.opcode)
        {
            case 0x58: /* IF */
                nIfs++;
                break;

            case 0x59: /* EIF */
                nIfs--;
                break;
        }
    }
    while (nIfs != 0);
}


/*************************************************************************/
/*                                                                       */
/* DEFINING AND USING FUNCTIONS AND INSTRUCTIONS                         */
/*                                                                       */
/*   Instructions appear in the specification's order.                   */
/*                                                                       */
/*************************************************************************/


/*************************************************************************/
/*                                                                       */
/* FDEF[]:       Function DEFinition                                     */
/* Opcode range: 0x2C                                                    */
/* Stack:        uint32 -->                                              */
/*                                                                       */
static void
Ins_FDEF(INS_ARG)
{
    FT_ULong n;
    TT_DefRecord* rec;
    TT_DefRecord* limit;


    /* some font programs are broken enough to redefine functions! */
    /* We will then parse the current table.                       */

    rec = CUR.FDefs;
    limit = rec + CUR.numFDefs;
    n = args[0];

    for (; rec < limit; rec++)
    {
        if (rec->opc == n)
            break;
    }

    if (rec == limit)
    {
        /* check that there is enough room for new functions */
        if (CUR.numFDefs >= CUR.maxFDefs)
        {
            CUR.error = TT_Err_Too_Many_Function_Defs;
            return;
        }
        CUR.numFDefs++;
    }

    /* Although FDEF takes unsigned 32-bit integer,  */
    /* func # must be within unsigned 16-bit integer */
    if (n > 0xFFFFU)
    {
        CUR.error = TT_Err_Too_Many_Function_Defs;
        return;
    }

    rec->range = CUR.curRange;
    rec->opc = (FT_UInt16)n;
    rec->start = CUR.IP + 1;
    rec->active = TRUE;

    if (n > CUR.maxFunc)
        CUR.maxFunc = (FT_UInt16)n;

    /* Now skip the whole function definition. */
    /* We don't allow nested IDEFS & FDEFs.    */

    while (SKIP_Code() == SUCCESS)
    {
        switch (CUR.opcode)
        {
            case 0x89: /* IDEF */
            case 0x2C: /* FDEF */
                CUR.error = TT_Err_Nested_DEFS;
                return;

            case 0x2D: /* ENDF */
                return;
        }
    }
}


/*************************************************************************/
/*                                                                       */
/* ENDF[]:       END Function definition                                 */
/* Opcode range: 0x2D                                                    */
/* Stack:        -->                                                     */
/*                                                                       */
static void
Ins_ENDF(INS_ARG)
{
    TT_CallRec* pRec;

    FT_UNUSED_ARG;


    if (CUR.callTop <= 0)       /* We encountered an ENDF without a call */
    {
        CUR.error = TT_Err_ENDF_In_Exec_Stream;
        return;
    }

    CUR.callTop--;

    pRec = &CUR.callStack[CUR.callTop];

    pRec->Cur_Count--;

    CUR.step_ins = FALSE;

    if (pRec->Cur_Count > 0)
    {
        CUR.callTop++;
        CUR.IP = pRec->Cur_Restart;
    }
    else
        /* Loop through the current function */
        INS_Goto_CodeRange(pRec->Caller_Range,
                           pRec->Caller_IP);

    /* Exit the current call frame.                      */

    /* NOTE: If the last instruction of a program is a   */
    /*       CALL or LOOPCALL, the return address is     */
    /*       always out of the code range.  This is a    */
    /*       valid address, and it is why we do not test */
    /*       the result of Ins_Goto_CodeRange() here!    */
}


/*************************************************************************/
/*                                                                       */
/* CALL[]:       CALL function                                           */
/* Opcode range: 0x2B                                                    */
/* Stack:        uint32? -->                                             */
/*                                                                       */
static void
Ins_CALL(INS_ARG)
{
    FT_ULong F;
    TT_CallRec* pCrec;
    TT_DefRecord* def;


    /* first of all, check the index */

    F = args[0];
    if (BOUNDS(F, CUR.maxFunc + 1))
        goto Fail;

    /* Except for some old Apple fonts, all functions in a TrueType */
    /* font are defined in increasing order, starting from 0.  This */
    /* means that we normally have                                  */
    /*                                                              */
    /*    CUR.maxFunc+1 == CUR.numFDefs                             */
    /*    CUR.FDefs[n].opc == n for n in 0..CUR.maxFunc             */
    /*                                                              */
    /* If this isn't true, we need to look up the function table.   */

    def = CUR.FDefs + F;
    if (CUR.maxFunc + 1 != CUR.numFDefs || def->opc != F)
    {
        /* look up the FDefs table */
        TT_DefRecord* limit;


        def = CUR.FDefs;
        limit = def + CUR.numFDefs;

        while (def < limit && def->opc != F)
            def++;

        if (def == limit)
            goto Fail;
    }

    /* check that the function is active */
    if (!def->active)
        goto Fail;

    /* check the call stack */
    if (CUR.callTop >= CUR.callSize)
    {
        CUR.error = TT_Err_Stack_Overflow;
        return;
    }

    pCrec = CUR.callStack + CUR.callTop;

    pCrec->Caller_Range = CUR.curRange;
    pCrec->Caller_IP = CUR.IP + 1;
    pCrec->Cur_Count = 1;
    pCrec->Cur_Restart = def->start;

    CUR.callTop++;

    INS_Goto_CodeRange(def->range,
                       def->start);

    CUR.step_ins = FALSE;
    return;

Fail:
    CUR.error = TT_Err_Invalid_Reference;
}


/*************************************************************************/
/*                                                                       */
/* LOOPCALL[]:   LOOP and CALL function                                  */
/* Opcode range: 0x2A                                                    */
/* Stack:        uint32? Eint16? -->                                     */
/*                                                                       */
static void
Ins_LOOPCALL(INS_ARG)
{
    FT_ULong F;
    TT_CallRec* pCrec;
    TT_DefRecord* def;


    /* first of all, check the index */
    F = args[1];
    if (BOUNDS(F, CUR.maxFunc + 1))
        goto Fail;

    /* Except for some old Apple fonts, all functions in a TrueType */
    /* font are defined in increasing order, starting from 0.  This */
    /* means that we normally have                                  */
    /*                                                              */
    /*    CUR.maxFunc+1 == CUR.numFDefs                             */
    /*    CUR.FDefs[n].opc == n for n in 0..CUR.maxFunc             */
    /*                                                              */
    /* If this isn't true, we need to look up the function table.   */

    def = CUR.FDefs + F;
    if (CUR.maxFunc + 1 != CUR.numFDefs || def->opc != F)
    {
        /* look up the FDefs table */
        TT_DefRecord* limit;


        def = CUR.FDefs;
        limit = def + CUR.numFDefs;

        while (def < limit && def->opc != F)
            def++;

        if (def == limit)
            goto Fail;
    }

    /* check that the function is active */
    if (!def->active)
        goto Fail;

    /* check stack */
    if (CUR.callTop >= CUR.callSize)
    {
        CUR.error = TT_Err_Stack_Overflow;
        return;
    }

    if (args[0] > 0)
    {
        pCrec = CUR.callStack + CUR.callTop;

        pCrec->Caller_Range = CUR.curRange;
        pCrec->Caller_IP = CUR.IP + 1;
        pCrec->Cur_Count = (FT_Int)args[0];
        pCrec->Cur_Restart = def->start;

        CUR.callTop++;

        INS_Goto_CodeRange(def->range, def->start);

        CUR.step_ins = FALSE;
    }
    return;

Fail:
    CUR.error = TT_Err_Invalid_Reference;
}


/*************************************************************************/
/*                                                                       */
/* IDEF[]:       Instruction DEFinition                                  */
/* Opcode range: 0x89                                                    */
/* Stack:        Eint8 -->                                               */
/*                                                                       */
static void
Ins_IDEF(INS_ARG)
{
    TT_DefRecord* def;
    TT_DefRecord* limit;


    /*  First of all, look for the same function in our table */

    def = CUR.IDefs;
    limit = def + CUR.numIDefs;

    for (; def < limit; def++)
        if (def->opc == (FT_ULong)args[0])
            break;

    if (def == limit)
    {
        /* check that there is enough room for a new instruction */
        if (CUR.numIDefs >= CUR.maxIDefs)
        {
            CUR.error = TT_Err_Too_Many_Instruction_Defs;
            return;
        }
        CUR.numIDefs++;
    }

    /* opcode must be unsigned 8-bit integer */
    if (0 > args[0] || args[0] > 0x00FF)
    {
        CUR.error = TT_Err_Too_Many_Instruction_Defs;
        return;
    }

    def->opc = (FT_Byte)args[0];
    def->start = CUR.IP + 1;
    def->range = CUR.curRange;
    def->active = TRUE;

    if ((FT_ULong)args[0] > CUR.maxIns)
        CUR.maxIns = (FT_Byte)args[0];

    /* Now skip the whole function definition. */
    /* We don't allow nested IDEFs & FDEFs.    */

    while (SKIP_Code() == SUCCESS)
    {
        switch (CUR.opcode)
        {
            case 0x89: /* IDEF */
            case 0x2C: /* FDEF */
                CUR.error = TT_Err_Nested_DEFS;
                return;
            case 0x2D: /* ENDF */
                return;
        }
    }
}


/*************************************************************************/
/*                                                                       */
/* PUSHING DATA ONTO THE INTERPRETER STACK                               */
/*                                                                       */
/*   Instructions appear in the specification's order.                   */
/*                                                                       */
/*************************************************************************/


/*************************************************************************/
/*                                                                       */
/* NPUSHB[]:     PUSH N Bytes                                            */
/* Opcode range: 0x40                                                    */
/* Stack:        --> uint32...                                           */
/*                                                                       */
static void
Ins_NPUSHB(INS_ARG)
{
    FT_UShort L, K;


    L = (FT_UShort)CUR.code[CUR.IP + 1];

    if (BOUNDS(L, CUR.stackSize + 1 - CUR.top))
    {
        CUR.error = TT_Err_Stack_Overflow;
        return;
    }

    for (K = 1; K <= L; K++)
        args[K - 1] = CUR.code[CUR.IP + K + 1];

    CUR.new_top += L;
}


/*************************************************************************/
/*                                                                       */
/* NPUSHW[]:     PUSH N Words                                            */
/* Opcode range: 0x41                                                    */
/* Stack:        --> int32...                                            */
/*                                                                       */
static void
Ins_NPUSHW(INS_ARG)
{
    FT_UShort L, K;


    L = (FT_UShort)CUR.code[CUR.IP + 1];

    if (BOUNDS(L, CUR.stackSize + 1 - CUR.top))
    {
        CUR.error = TT_Err_Stack_Overflow;
        return;
    }

    CUR.IP += 2;

    for (K = 0; K < L; K++)
        args[K] = GET_ShortIns();

    CUR.step_ins = FALSE;
    CUR.new_top += L;
}


/*************************************************************************/
/*                                                                       */
/* PUSHB[abc]:   PUSH Bytes                                              */
/* Opcode range: 0xB0-0xB7                                               */
/* Stack:        --> uint32...                                           */
/*                                                                       */
static void
Ins_PUSHB(INS_ARG)
{
    FT_UShort L, K;


    L = (FT_UShort)(CUR.opcode - 0xB0 + 1);

    if (BOUNDS(L, CUR.stackSize + 1 - CUR.top))
    {
        CUR.error = TT_Err_Stack_Overflow;
        return;
    }

    for (K = 1; K <= L; K++)
        args[K - 1] = CUR.code[CUR.IP + K];
}


/*************************************************************************/
/*                                                                       */
/* PUSHW[abc]:   PUSH Words                                              */
/* Opcode range: 0xB8-0xBF                                               */
/* Stack:        --> int32...                                            */
/*                                                                       */
static void
Ins_PUSHW(INS_ARG)
{
    FT_UShort L, K;


    L = (FT_UShort)(CUR.opcode - 0xB8 + 1);

    if (BOUNDS(L, CUR.stackSize + 1 - CUR.top))
    {
        CUR.error = TT_Err_Stack_Overflow;
        return;
    }

    CUR.IP++;

    for (K = 0; K < L; K++)
        args[K] = GET_ShortIns();

    CUR.step_ins = FALSE;
}


/*************************************************************************/
/*                                                                       */
/* MANAGING THE GRAPHICS STATE                                           */
/*                                                                       */
/*  Instructions appear in the specs' order.                             */
/*                                                                       */
/*************************************************************************/


/*************************************************************************/
/*                                                                       */
/* GC[a]:        Get Coordinate projected onto                           */
/* Opcode range: 0x46-0x47                                               */
/* Stack:        uint32 --> f26.6                                        */
/*                                                                       */
/* BULLSHIT: Measures from the original glyph must be taken along the    */
/*           dual projection vector!                                     */
/*                                                                       */
static void
Ins_GC(INS_ARG)
{
    FT_ULong L;
    FT_F26Dot6 R;


    L = (FT_ULong)args[0];

    if (BOUNDS(L, CUR.zp2.n_points))
    {
        if (CUR.pedantic_hinting)
        {
            CUR.error = TT_Err_Invalid_Reference;
            return;
        }
        else
            R = 0;
    }
    else
    {
        if (CUR.opcode & 1)
            R = CUR_fast_dualproj(&CUR.zp2.org[L]);
        else
            R = CUR_fast_project(&CUR.zp2.cur[L]);
    }

    args[0] = R;
}


/*************************************************************************/
/*                                                                       */
/* SCFS[]:       Set Coordinate From Stack                               */
/* Opcode range: 0x48                                                    */
/* Stack:        f26.6 uint32 -->                                        */
/*                                                                       */
/* Formula:                                                              */
/*                                                                       */
/*   OA := OA + ( value - OA.p )/( f.p ) * f                             */
/*                                                                       */
static void
Ins_SCFS(INS_ARG)
{
    FT_Long K;
    FT_UShort L;


    L = (FT_UShort)args[0];

    if (BOUNDS(L, CUR.zp2.n_points))
    {
        if (CUR.pedantic_hinting)
            CUR.error = TT_Err_Invalid_Reference;
        return;
    }

    K = CUR_fast_project(&CUR.zp2.cur[L]);

    CUR_Func_move(&CUR.zp2, L, args[1] - K);

    /* not part of the specs, but here for safety */

    if (CUR.GS.gep2 == 0)
        CUR.zp2.org[L] = CUR.zp2.cur[L];
}


/*************************************************************************/
/*                                                                       */
/* MD[a]:        Measure Distance                                        */
/* Opcode range: 0x49-0x4A                                               */
/* Stack:        uint32 uint32 --> f26.6                                 */
/*                                                                       */
/* BULLSHIT: Measure taken in the original glyph must be along the dual  */
/*           projection vector.                                          */
/*                                                                       */
/* Second BULLSHIT: Flag attributes are inverted!                        */
/*                  0 => measure distance in original outline            */
/*                  1 => measure distance in grid-fitted outline         */
/*                                                                       */
/* Third one: `zp0 - zp1', and not `zp2 - zp1!                           */
/*                                                                       */
static void
Ins_MD(INS_ARG)
{
    FT_UShort K, L;
    FT_F26Dot6 D;


    K = (FT_UShort)args[1];
    L = (FT_UShort)args[0];

    if (BOUNDS(L, CUR.zp0.n_points) ||
        BOUNDS(K, CUR.zp1.n_points))
    {
        if (CUR.pedantic_hinting)
        {
            CUR.error = TT_Err_Invalid_Reference;
            return;
        }
        D = 0;
    }
    else
    {
        if (CUR.opcode & 1)
            D = CUR_Func_project(CUR.zp0.cur + L, CUR.zp1.cur + K);
        else
        {
            FT_Vector* vec1 = CUR.zp0.orus + L;
            FT_Vector* vec2 = CUR.zp1.orus + K;


            if (CUR.metrics.x_scale == CUR.metrics.y_scale)
            {
                /* this should be faster */
                D = CUR_Func_dualproj(vec1, vec2);
                D = TT_MULFIX(D, CUR.metrics.x_scale);
            }
            else
            {
                FT_Vector vec;


                vec.x = TT_MULFIX(vec1->x - vec2->x, CUR.metrics.x_scale);
                vec.y = TT_MULFIX(vec1->y - vec2->y, CUR.metrics.y_scale);

                D = CUR_fast_dualproj(&vec);
            }
        }
    }

    args[0] = D;
}


/*************************************************************************/
/*                                                                       */
/* SDPVTL[a]:    Set Dual PVector to Line                                */
/* Opcode range: 0x86-0x87                                               */
/* Stack:        uint32 uint32 -->                                       */
/*                                                                       */
static void
Ins_SDPVTL(INS_ARG)
{
    FT_Long A, B, C;
    FT_UShort p1, p2;    /* was FT_Int in pas type ERROR */


    p1 = (FT_UShort)args[1];
    p2 = (FT_UShort)args[0];

    if (BOUNDS(p2, CUR.zp1.n_points) ||
        BOUNDS(p1, CUR.zp2.n_points))
    {
        if (CUR.pedantic_hinting)
            CUR.error = TT_Err_Invalid_Reference;
        return;
    }

    {
        FT_Vector* v1 = CUR.zp1.org + p2;
        FT_Vector* v2 = CUR.zp2.org + p1;


        A = v1->x - v2->x;
        B = v1->y - v2->y;
    }

    if ((CUR.opcode & 1) != 0)
    {
        C = B;  /* counter clockwise rotation */
        B = A;
        A = -C;
    }

    NORMalize(A, B, &CUR.GS.dualVector);

    {
        FT_Vector* v1 = CUR.zp1.cur + p2;
        FT_Vector* v2 = CUR.zp2.cur + p1;


        A = v1->x - v2->x;
        B = v1->y - v2->y;
    }

    if ((CUR.opcode & 1) != 0)
    {
        C = B;  /* counter clockwise rotation */
        B = A;
        A = -C;
    }

    NORMalize(A, B, &CUR.GS.projVector);

    GUESS_VECTOR(freeVector);

    COMPUTE_Funcs();
}


/*************************************************************************/
/*                                                                       */
/* SZP0[]:       Set Zone Pointer 0                                      */
/* Opcode range: 0x13                                                    */
/* Stack:        uint32 -->                                              */
/*                                                                       */
static void
Ins_SZP0(INS_ARG)
{
    switch ((FT_Int)args[0])
    {
        case 0:
            CUR.zp0 = CUR.twilight;
            break;

        case 1:
            CUR.zp0 = CUR.pts;
            break;

        default:
            if (CUR.pedantic_hinting)
                CUR.error = TT_Err_Invalid_Reference;
            return;
    }

    CUR.GS.gep0 = (FT_UShort)args[0];
}


/*************************************************************************/
/*                                                                       */
/* SZP1[]:       Set Zone Pointer 1                                      */
/* Opcode range: 0x14                                                    */
/* Stack:        uint32 -->                                              */
/*                                                                       */
static void
Ins_SZP1(INS_ARG)
{
    switch ((FT_Int)args[0])
    {
        case 0:
            CUR.zp1 = CUR.twilight;
            break;

        case 1:
            CUR.zp1 = CUR.pts;
            break;

        default:
            if (CUR.pedantic_hinting)
                CUR.error = TT_Err_Invalid_Reference;
            return;
    }

    CUR.GS.gep1 = (FT_UShort)args[0];
}


/*************************************************************************/
/*                                                                       */
/* SZP2[]:       Set Zone Pointer 2                                      */
/* Opcode range: 0x15                                                    */
/* Stack:        uint32 -->                                              */
/*                                                                       */
static void
Ins_SZP2(INS_ARG)
{
    switch ((FT_Int)args[0])
    {
        case 0:
            CUR.zp2 = CUR.twilight;
            break;

        case 1:
            CUR.zp2 = CUR.pts;
            break;

        default:
            if (CUR.pedantic_hinting)
                CUR.error = TT_Err_Invalid_Reference;
            return;
    }

    CUR.GS.gep2 = (FT_UShort)args[0];
}


/*************************************************************************/
/*                                                                       */
/* SZPS[]:       Set Zone PointerS                                       */
/* Opcode range: 0x16                                                    */
/* Stack:        uint32 -->                                              */
/*                                                                       */
static void
Ins_SZPS(INS_ARG)
{
    switch ((FT_Int)args[0])
    {
        case 0:
            CUR.zp0 = CUR.twilight;
            break;

        case 1:
            CUR.zp0 = CUR.pts;
            break;

        default:
            if (CUR.pedantic_hinting)
                CUR.error = TT_Err_Invalid_Reference;
            return;
    }

    CUR.zp1 = CUR.zp0;
    CUR.zp2 = CUR.zp0;

    CUR.GS.gep0 = (FT_UShort)args[0];
    CUR.GS.gep1 = (FT_UShort)args[0];
    CUR.GS.gep2 = (FT_UShort)args[0];
}


/*************************************************************************/
/*                                                                       */
/* INSTCTRL[]:   INSTruction ConTRoL                                     */
/* Opcode range: 0x8e                                                    */
/* Stack:        int32 int32 -->                                         */
/*                                                                       */
static void
Ins_INSTCTRL(INS_ARG)
{
    FT_Long K, L;


    K = args[1];
    L = args[0];

    if (K < 1 || K > 2)
    {
        if (CUR.pedantic_hinting)
            CUR.error = TT_Err_Invalid_Reference;
        return;
    }

    if (L != 0)
        L = K;

    CUR.GS.instruct_control = FT_BOOL(
        ((FT_Byte)CUR.GS.instruct_control & ~(FT_Byte)K) | (FT_Byte)L);
}


/*************************************************************************/
/*                                                                       */
/* SCANCTRL[]:   SCAN ConTRoL                                            */
/* Opcode range: 0x85                                                    */
/* Stack:        uint32? -->                                             */
/*                                                                       */
static void
Ins_SCANCTRL(INS_ARG)
{
    FT_Int A;


    /* Get Threshold */
    A = (FT_Int)(args[0] & 0xFF);

    if (A == 0xFF)
    {
        CUR.GS.scan_control = TRUE;
        return;
    }
    else if (A == 0)
    {
        CUR.GS.scan_control = FALSE;
        return;
    }

    if ((args[0] & 0x100) != 0 && CUR.tt_metrics.ppem <= A)
        CUR.GS.scan_control = TRUE;

    if ((args[0] & 0x200) != 0 && CUR.tt_metrics.rotated)
        CUR.GS.scan_control = TRUE;

    if ((args[0] & 0x400) != 0 && CUR.tt_metrics.stretched)
        CUR.GS.scan_control = TRUE;

    if ((args[0] & 0x800) != 0 && CUR.tt_metrics.ppem > A)
        CUR.GS.scan_control = FALSE;

    if ((args[0] & 0x1000) != 0 && CUR.tt_metrics.rotated)
        CUR.GS.scan_control = FALSE;

    if ((args[0] & 0x2000) != 0 && CUR.tt_metrics.stretched)
        CUR.GS.scan_control = FALSE;
}


/*************************************************************************/
/*                                                                       */
/* SCANTYPE[]:   SCAN TYPE                                               */
/* Opcode range: 0x8D                                                    */
/* Stack:        uint32? -->                                             */
/*                                                                       */
static void
Ins_SCANTYPE(INS_ARG)
{
    if (args[0] >= 0)
        CUR.GS.scan_type = (FT_Int)args[0];
}


/*************************************************************************/
/*                                                                       */
/* MANAGING OUTLINES                                                     */
/*                                                                       */
/*   Instructions appear in the specification's order.                   */
/*                                                                       */
/*************************************************************************/


/*************************************************************************/
/*                                                                       */
/* FLIPPT[]:     FLIP PoinT                                              */
/* Opcode range: 0x80                                                    */
/* Stack:        uint32... -->                                           */
/*                                                                       */
static void
Ins_FLIPPT(INS_ARG)
{
    FT_UShort point;

    FT_UNUSED_ARG;


    if (CUR.top < CUR.GS.loop)
    {
        CUR.error = TT_Err_Too_Few_Arguments;
        return;
    }

    while (CUR.GS.loop > 0)
    {
        CUR.args--;

        point = (FT_UShort)CUR.stack[CUR.args];

        if (BOUNDS(point, CUR.pts.n_points))
        {
            if (CUR.pedantic_hinting)
            {
                CUR.error = TT_Err_Invalid_Reference;
                return;
            }
        }
        else
            CUR.pts.tags[point] ^= FT_CURVE_TAG_ON;

        CUR.GS.loop--;
    }

    CUR.GS.loop = 1;
    CUR.new_top = CUR.args;
}


/*************************************************************************/
/*                                                                       */
/* FLIPRGON[]:   FLIP RanGe ON                                           */
/* Opcode range: 0x81                                                    */
/* Stack:        uint32 uint32 -->                                       */
/*                                                                       */
static void
Ins_FLIPRGON(INS_ARG)
{
    FT_UShort I, K, L;


    K = (FT_UShort)args[1];
    L = (FT_UShort)args[0];

    if (BOUNDS(K, CUR.pts.n_points) ||
        BOUNDS(L, CUR.pts.n_points))
    {
        if (CUR.pedantic_hinting)
            CUR.error = TT_Err_Invalid_Reference;
        return;
    }

    for (I = L; I <= K; I++)
        CUR.pts.tags[I] |= FT_CURVE_TAG_ON;
}


/*************************************************************************/
/*                                                                       */
/* FLIPRGOFF:    FLIP RanGe OFF                                          */
/* Opcode range: 0x82                                                    */
/* Stack:        uint32 uint32 -->                                       */
/*                                                                       */
static void
Ins_FLIPRGOFF(INS_ARG)
{
    FT_UShort I, K, L;


    K = (FT_UShort)args[1];
    L = (FT_UShort)args[0];

    if (BOUNDS(K, CUR.pts.n_points) ||
        BOUNDS(L, CUR.pts.n_points))
    {
        if (CUR.pedantic_hinting)
            CUR.error = TT_Err_Invalid_Reference;
        return;
    }

    for (I = L; I <= K; I++)
        CUR.pts.tags[I] &= ~FT_CURVE_TAG_ON;
}


static FT_Bool
Compute_Point_Displacement(EXEC_OP_ FT_F26Dot6* x,
                           FT_F26Dot6* y,
                           TT_GlyphZone zone,
                           FT_UShort* refp)
{
    TT_GlyphZoneRec zp;
    FT_UShort p;
    FT_F26Dot6 d;


    if (CUR.opcode & 1)
    {
        zp = CUR.zp0;
        p = CUR.GS.rp1;
    }
    else
    {
        zp = CUR.zp1;
        p = CUR.GS.rp2;
    }

    if (BOUNDS(p, zp.n_points))
    {
        if (CUR.pedantic_hinting)
            CUR.error = TT_Err_Invalid_Reference;
        *refp = 0;
        return FAILURE;
    }

    *zone = zp;
    *refp = p;

    d = CUR_Func_project(zp.cur + p, zp.org + p);

    #ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
    if (CUR.face->unpatented_hinting)
    {
        if (CUR.GS.both_x_axis)
        {
            *x = d;
            *y = 0;
        }
        else
        {
            *x = 0;
            *y = d;
        }
    }
    else
    #endif
    {
        *x = TT_MULDIV(d,
                       (FT_Long)CUR.GS.freeVector.x * 0x10000L,
                       CUR.F_dot_P);
        *y = TT_MULDIV(d,
                       (FT_Long)CUR.GS.freeVector.y * 0x10000L,
                       CUR.F_dot_P);
    }

    return SUCCESS;
}


static void
Move_Zp2_Point(EXEC_OP_ FT_UShort point,
               FT_F26Dot6 dx,
               FT_F26Dot6 dy,
               FT_Bool touch)
{
    #ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
    if (CUR.face->unpatented_hinting)
    {
        if (CUR.GS.both_x_axis)
        {
            CUR.zp2.cur[point].x += dx;
            if (touch)
                CUR.zp2.tags[point] |= FT_CURVE_TAG_TOUCH_X;
        }
        else
        {
            CUR.zp2.cur[point].y += dy;
            if (touch)
                CUR.zp2.tags[point] |= FT_CURVE_TAG_TOUCH_Y;
        }
        return;
    }
    #endif

    if (CUR.GS.freeVector.x != 0)
    {
        CUR.zp2.cur[point].x += dx;
        if (touch)
            CUR.zp2.tags[point] |= FT_CURVE_TAG_TOUCH_X;
    }

    if (CUR.GS.freeVector.y != 0)
    {
        CUR.zp2.cur[point].y += dy;
        if (touch)
            CUR.zp2.tags[point] |= FT_CURVE_TAG_TOUCH_Y;
    }
}


/*************************************************************************/
/*                                                                       */
/* SHP[a]:       SHift Point by the last point                           */
/* Opcode range: 0x32-0x33                                               */
/* Stack:        uint32... -->                                           */
/*                                                                       */
static void
Ins_SHP(INS_ARG)
{
    TT_GlyphZoneRec zp;
    FT_UShort refp;

    FT_F26Dot6 dx,
               dy;
    FT_UShort point;

    FT_UNUSED_ARG;


    if (CUR.top < CUR.GS.loop)
    {
        CUR.error = TT_Err_Invalid_Reference;
        return;
    }

    if (COMPUTE_Point_Displacement(&dx, &dy, &zp, &refp))
        return;

    while (CUR.GS.loop > 0)
    {
        CUR.args--;
        point = (FT_UShort)CUR.stack[CUR.args];

        if (BOUNDS(point, CUR.zp2.n_points))
        {
            if (CUR.pedantic_hinting)
            {
                CUR.error = TT_Err_Invalid_Reference;
                return;
            }
        }
        else
            /* XXX: UNDOCUMENTED! SHP touches the points */
            MOVE_Zp2_Point(point, dx, dy, TRUE);

        CUR.GS.loop--;
    }

    CUR.GS.loop = 1;
    CUR.new_top = CUR.args;
}


/*************************************************************************/
/*                                                                       */
/* SHC[a]:       SHift Contour                                           */
/* Opcode range: 0x34-35                                                 */
/* Stack:        uint32 -->                                              */
/*                                                                       */
static void
Ins_SHC(INS_ARG)
{
    TT_GlyphZoneRec zp;
    FT_UShort refp;
    FT_F26Dot6 dx,
               dy;

    FT_Short contour;
    FT_UShort first_point, last_point, i;


    contour = (FT_UShort)args[0];

    if (BOUNDS(contour, CUR.pts.n_contours))
    {
        if (CUR.pedantic_hinting)
            CUR.error = TT_Err_Invalid_Reference;
        return;
    }

    if (COMPUTE_Point_Displacement(&dx, &dy, &zp, &refp))
        return;

    if (contour == 0)
        first_point = 0;
    else
        first_point = (FT_UShort)(CUR.pts.contours[contour - 1] + 1 -
                                  CUR.pts.first_point);

    last_point = (FT_UShort)(CUR.pts.contours[contour] -
                             CUR.pts.first_point);

    /* XXX: this is probably wrong... at least it prevents memory */
    /*      corruption when zp2 is the twilight zone              */
    if (BOUNDS(last_point, CUR.zp2.n_points))
    {
        if (CUR.zp2.n_points > 0)
            last_point = (FT_UShort)(CUR.zp2.n_points - 1);
        else
            last_point = 0;
    }

    /* XXX: UNDOCUMENTED! SHC touches the points */
    for (i = first_point; i <= last_point; i++)
    {
        if (zp.cur != CUR.zp2.cur || refp != i)
            MOVE_Zp2_Point(i, dx, dy, TRUE);
    }
}


/*************************************************************************/
/*                                                                       */
/* SHZ[a]:       SHift Zone                                              */
/* Opcode range: 0x36-37                                                 */
/* Stack:        uint32 -->                                              */
/*                                                                       */
static void
Ins_SHZ(INS_ARG)
{
    TT_GlyphZoneRec zp;
    FT_UShort refp;
    FT_F26Dot6 dx,
               dy;

    FT_UShort last_point, i;


    if (BOUNDS(args[0], 2))
    {
        if (CUR.pedantic_hinting)
            CUR.error = TT_Err_Invalid_Reference;
        return;
    }

    if (COMPUTE_Point_Displacement(&dx, &dy, &zp, &refp))
        return;

    /* XXX: UNDOCUMENTED! SHZ doesn't move the phantom points.  */
    /*      Twilight zone has no contours, so use `n_points'.   */
    /*      Normal zone's `n_points' includes phantoms, so must */
    /*      use end of last contour.                            */
    if (CUR.GS.gep2 == 0 && CUR.zp2.n_points > 0)
        last_point = (FT_UShort)(CUR.zp2.n_points - 1);
    else if (CUR.GS.gep2 == 1 && CUR.zp2.n_contours > 0)
        last_point = (FT_UShort)(CUR.zp2.contours[CUR.zp2.n_contours - 1]);
    else
        last_point = 0;

    /* XXX: UNDOCUMENTED! SHZ doesn't touch the points */
    for (i = 0; i <= last_point; i++)
    {
        if (zp.cur != CUR.zp2.cur || refp != i)
            MOVE_Zp2_Point(i, dx, dy, FALSE);
    }
}


/*************************************************************************/
/*                                                                       */
/* SHPIX[]:      SHift points by a PIXel amount                          */
/* Opcode range: 0x38                                                    */
/* Stack:        f26.6 uint32... -->                                     */
/*                                                                       */
static void
Ins_SHPIX(INS_ARG)
{
    FT_F26Dot6 dx, dy;
    FT_UShort point;


    if (CUR.top < CUR.GS.loop + 1)
    {
        CUR.error = TT_Err_Invalid_Reference;
        return;
    }

    #ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
    if (CUR.face->unpatented_hinting)
    {
        if (CUR.GS.both_x_axis)
        {
            dx = TT_MulFix14((FT_UInt32)args[0], 0x4000);
            dy = 0;
        }
        else
        {
            dx = 0;
            dy = TT_MulFix14((FT_UInt32)args[0], 0x4000);
        }
    }
    else
    #endif
    {
        dx = TT_MulFix14((FT_UInt32)args[0], CUR.GS.freeVector.x);
        dy = TT_MulFix14((FT_UInt32)args[0], CUR.GS.freeVector.y);
    }

    while (CUR.GS.loop > 0)
    {
        CUR.args--;

        point = (FT_UShort)CUR.stack[CUR.args];

        if (BOUNDS(point, CUR.zp2.n_points))
        {
            if (CUR.pedantic_hinting)
            {
                CUR.error = TT_Err_Invalid_Reference;
                return;
            }
        }
        else
            MOVE_Zp2_Point(point, dx, dy, TRUE);

        CUR.GS.loop--;
    }

    CUR.GS.loop = 1;
    CUR.new_top = CUR.args;
}


/*************************************************************************/
/*                                                                       */
/* MSIRP[a]:     Move Stack Indirect Relative Position                   */
/* Opcode range: 0x3A-0x3B                                               */
/* Stack:        f26.6 uint32 -->                                        */
/*                                                                       */
static void
Ins_MSIRP(INS_ARG)
{
    FT_UShort point;
    FT_F26Dot6 distance;


    point = (FT_UShort)args[0];

    if (BOUNDS(point, CUR.zp1.n_points) ||
        BOUNDS(CUR.GS.rp0, CUR.zp0.n_points))
    {
        if (CUR.pedantic_hinting)
            CUR.error = TT_Err_Invalid_Reference;
        return;
    }

    /* XXX: UNDOCUMENTED! behaviour */
    if (CUR.GS.gep1 == 0)     /* if the point that is to be moved */
                              /* is in twilight zone              */
    {
        CUR.zp1.org[point] = CUR.zp0.org[CUR.GS.rp0];
        CUR_Func_move_orig(&CUR.zp1, point, args[1]);
        CUR.zp1.cur[point] = CUR.zp1.org[point];
    }

    distance = CUR_Func_project(CUR.zp1.cur + point,
                                CUR.zp0.cur + CUR.GS.rp0);

    CUR_Func_move(&CUR.zp1, point, args[1] - distance);

    CUR.GS.rp1 = CUR.GS.rp0;
    CUR.GS.rp2 = point;

    if ((CUR.opcode & 1) != 0)
        CUR.GS.rp0 = point;
}


/*************************************************************************/
/*                                                                       */
/* MDAP[a]:      Move Direct Absolute Point                              */
/* Opcode range: 0x2E-0x2F                                               */
/* Stack:        uint32 -->                                              */
/*                                                                       */
static void
Ins_MDAP(INS_ARG)
{
    FT_UShort point;
    FT_F26Dot6 cur_dist,
               distance;


    point = (FT_UShort)args[0];

    if (BOUNDS(point, CUR.zp0.n_points))
    {
        if (CUR.pedantic_hinting)
            CUR.error = TT_Err_Invalid_Reference;
        return;
    }

    /* XXX: Is there some undocumented feature while in the */
    /*      twilight zone? ?                                */
    if ((CUR.opcode & 1) != 0)
    {
        cur_dist = CUR_fast_project(&CUR.zp0.cur[point]);
        distance = CUR_Func_round(cur_dist,
                                  CUR.tt_metrics.compensations[0]) - cur_dist;
    }
    else
        distance = 0;

    CUR_Func_move(&CUR.zp0, point, distance);

    CUR.GS.rp0 = point;
    CUR.GS.rp1 = point;
}


/*************************************************************************/
/*                                                                       */
/* MIAP[a]:      Move Indirect Absolute Point                            */
/* Opcode range: 0x3E-0x3F                                               */
/* Stack:        uint32 uint32 -->                                       */
/*                                                                       */
static void
Ins_MIAP(INS_ARG)
{
    FT_ULong cvtEntry;
    FT_UShort point;
    FT_F26Dot6 distance,
               org_dist;


    cvtEntry = (FT_ULong)args[1];
    point = (FT_UShort)args[0];

    if (BOUNDS(point, CUR.zp0.n_points) ||
        BOUNDS(cvtEntry, CUR.cvtSize))
    {
        if (CUR.pedantic_hinting)
            CUR.error = TT_Err_Invalid_Reference;
        return;
    }

    /* XXX: UNDOCUMENTED!                                */
    /*                                                   */
    /* The behaviour of an MIAP instruction is quite     */
    /* different when used in the twilight zone.         */
    /*                                                   */
    /* First, no control value cut-in test is performed  */
    /* as it would fail anyway.  Second, the original    */
    /* point, i.e. (org_x,org_y) of zp0.point, is set    */
    /* to the absolute, unrounded distance found in      */
    /* the CVT.                                          */
    /*                                                   */
    /* This is used in the CVT programs of the Microsoft */
    /* fonts Arial, Times, etc., in order to re-adjust   */
    /* some key font heights.  It allows the use of the  */
    /* IP instruction in the twilight zone, which        */
    /* otherwise would be `illegal' according to the     */
    /* specification.                                    */
    /*                                                   */
    /* We implement it with a special sequence for the   */
    /* twilight zone.  This is a bad hack, but it seems  */
    /* to work.                                          */

    distance = CUR_Func_read_cvt(cvtEntry);

    if (CUR.GS.gep0 == 0)     /* If in twilight zone */
    {
        CUR.zp0.org[point].x = TT_MulFix14((FT_UInt32)distance, CUR.GS.freeVector.x);
        CUR.zp0.org[point].y = TT_MulFix14((FT_UInt32)distance, CUR.GS.freeVector.y),
        CUR.zp0.cur[point] = CUR.zp0.org[point];
    }

    org_dist = CUR_fast_project(&CUR.zp0.cur[point]);

    if ((CUR.opcode & 1) != 0)       /* rounding and control cutin flag */
    {
        if (FT_ABS(distance - org_dist) > CUR.GS.control_value_cutin)
            distance = org_dist;

        distance = CUR_Func_round(distance, CUR.tt_metrics.compensations[0]);
    }

    CUR_Func_move(&CUR.zp0, point, distance - org_dist);

    CUR.GS.rp0 = point;
    CUR.GS.rp1 = point;
}


/*************************************************************************/
/*                                                                       */
/* MDRP[abcde]:  Move Direct Relative Point                              */
/* Opcode range: 0xC0-0xDF                                               */
/* Stack:        uint32 -->                                              */
/*                                                                       */
static void
Ins_MDRP(INS_ARG)
{
    FT_UShort point;
    FT_F26Dot6 org_dist, distance;


    point = (FT_UShort)args[0];

    if (BOUNDS(point, CUR.zp1.n_points) ||
        BOUNDS(CUR.GS.rp0, CUR.zp0.n_points))
    {
        if (CUR.pedantic_hinting)
            CUR.error = TT_Err_Invalid_Reference;
        return;
    }

    /* XXX: Is there some undocumented feature while in the */
    /*      twilight zone?                                  */

    /* XXX: UNDOCUMENTED: twilight zone special case */

    if (CUR.GS.gep0 == 0 || CUR.GS.gep1 == 0)
    {
        FT_Vector* vec1 = &CUR.zp1.org[point];
        FT_Vector* vec2 = &CUR.zp0.org[CUR.GS.rp0];


        org_dist = CUR_Func_dualproj(vec1, vec2);
    }
    else
    {
        FT_Vector* vec1 = &CUR.zp1.orus[point];
        FT_Vector* vec2 = &CUR.zp0.orus[CUR.GS.rp0];


        if (CUR.metrics.x_scale == CUR.metrics.y_scale)
        {
            /* this should be faster */
            org_dist = CUR_Func_dualproj(vec1, vec2);
            org_dist = TT_MULFIX(org_dist, CUR.metrics.x_scale);
        }
        else
        {
            FT_Vector vec;


            vec.x = TT_MULFIX(vec1->x - vec2->x, CUR.metrics.x_scale);
            vec.y = TT_MULFIX(vec1->y - vec2->y, CUR.metrics.y_scale);

            org_dist = CUR_fast_dualproj(&vec);
        }
    }

    /* single width cut-in test */

    if (FT_ABS(org_dist - CUR.GS.single_width_value) <
        CUR.GS.single_width_cutin)
    {
        if (org_dist >= 0)
            org_dist = CUR.GS.single_width_value;
        else
            org_dist = -CUR.GS.single_width_value;
    }

    /* round flag */

    if ((CUR.opcode & 4) != 0)
        distance = CUR_Func_round(
            org_dist,
            CUR.tt_metrics.compensations[CUR.opcode & 3]);
    else
        distance = ROUND_None(
            org_dist,
            CUR.tt_metrics.compensations[CUR.opcode & 3]);

    /* minimum distance flag */

    if ((CUR.opcode & 8) != 0)
    {
        if (org_dist >= 0)
        {
            if (distance < CUR.GS.minimum_distance)
                distance = CUR.GS.minimum_distance;
        }
        else
        {
            if (distance > -CUR.GS.minimum_distance)
                distance = -CUR.GS.minimum_distance;
        }
    }

    /* now move the point */

    org_dist = CUR_Func_project(CUR.zp1.cur + point,
                                CUR.zp0.cur + CUR.GS.rp0);

    CUR_Func_move(&CUR.zp1, point, distance - org_dist);

    CUR.GS.rp1 = CUR.GS.rp0;
    CUR.GS.rp2 = point;

    if ((CUR.opcode & 16) != 0)
        CUR.GS.rp0 = point;
}


/*************************************************************************/
/*                                                                       */
/* MIRP[abcde]:  Move Indirect Relative Point                            */
/* Opcode range: 0xE0-0xFF                                               */
/* Stack:        int32? uint32 -->                                       */
/*                                                                       */
static void
Ins_MIRP(INS_ARG)
{
    FT_UShort point;
    FT_ULong cvtEntry;

    FT_F26Dot6 cvt_dist,
               distance,
               cur_dist,
               org_dist;


    point = (FT_UShort)args[0];
    cvtEntry = (FT_ULong)(args[1] + 1);

    /* XXX: UNDOCUMENTED! cvt[-1] = 0 always */

    if (BOUNDS(point, CUR.zp1.n_points) ||
        BOUNDS(cvtEntry, CUR.cvtSize + 1) ||
        BOUNDS(CUR.GS.rp0, CUR.zp0.n_points))
    {
        if (CUR.pedantic_hinting)
            CUR.error = TT_Err_Invalid_Reference;
        return;
    }

    if (!cvtEntry)
        cvt_dist = 0;
    else
        cvt_dist = CUR_Func_read_cvt(cvtEntry - 1);

    /* single width test */

    if (FT_ABS(cvt_dist - CUR.GS.single_width_value) <
        CUR.GS.single_width_cutin)
    {
        if (cvt_dist >= 0)
            cvt_dist = CUR.GS.single_width_value;
        else
            cvt_dist = -CUR.GS.single_width_value;
    }

    /* XXX: UNDOCUMENTED! -- twilight zone */

    if (CUR.GS.gep1 == 0)
    {
        CUR.zp1.org[point].x = CUR.zp0.org[CUR.GS.rp0].x +
                               TT_MulFix14((FT_UInt32)cvt_dist,
                                           CUR.GS.freeVector.x);

        CUR.zp1.org[point].y = CUR.zp0.org[CUR.GS.rp0].y +
                               TT_MulFix14((FT_UInt32)cvt_dist,
                                           CUR.GS.freeVector.y);

        CUR.zp1.cur[point] = CUR.zp0.cur[point];
    }

    org_dist = CUR_Func_dualproj(&CUR.zp1.org[point],
                                 &CUR.zp0.org[CUR.GS.rp0]);
    cur_dist = CUR_Func_project(&CUR.zp1.cur[point],
                                &CUR.zp0.cur[CUR.GS.rp0]);

    /* auto-flip test */

    if (CUR.GS.auto_flip)
    {
        if ((org_dist ^ cvt_dist) < 0)
            cvt_dist = -cvt_dist;
    }

    /* control value cutin and round */

    if ((CUR.opcode & 4) != 0)
    {
        /* XXX: UNDOCUMENTED!  Only perform cut-in test when both points */
        /*      refer to the same zone.                                  */

        if (CUR.GS.gep0 == CUR.GS.gep1)
            if (FT_ABS(cvt_dist - org_dist) >= CUR.GS.control_value_cutin)
                cvt_dist = org_dist;

        distance = CUR_Func_round(
            cvt_dist,
            CUR.tt_metrics.compensations[CUR.opcode & 3]);
    }
    else
        distance = ROUND_None(
            cvt_dist,
            CUR.tt_metrics.compensations[CUR.opcode & 3]);

    /* minimum distance test */

    if ((CUR.opcode & 8) != 0)
    {
        if (org_dist >= 0)
        {
            if (distance < CUR.GS.minimum_distance)
                distance = CUR.GS.minimum_distance;
        }
        else
        {
            if (distance > -CUR.GS.minimum_distance)
                distance = -CUR.GS.minimum_distance;
        }
    }

    CUR_Func_move(&CUR.zp1, point, distance - cur_dist);

    CUR.GS.rp1 = CUR.GS.rp0;

    if ((CUR.opcode & 16) != 0)
        CUR.GS.rp0 = point;

    /* XXX: UNDOCUMENTED! */
    CUR.GS.rp2 = point;
}


/*************************************************************************/
/*                                                                       */
/* ALIGNRP[]:    ALIGN Relative Point                                    */
/* Opcode range: 0x3C                                                    */
/* Stack:        uint32 uint32... -->                                    */
/*                                                                       */
static void
Ins_ALIGNRP(INS_ARG)
{
    FT_UShort point;
    FT_F26Dot6 distance;

    FT_UNUSED_ARG;


    if (CUR.top < CUR.GS.loop ||
        BOUNDS(CUR.GS.rp0, CUR.zp0.n_points))
    {
        if (CUR.pedantic_hinting)
            CUR.error = TT_Err_Invalid_Reference;
        return;
    }

    while (CUR.GS.loop > 0)
    {
        CUR.args--;

        point = (FT_UShort)CUR.stack[CUR.args];

        if (BOUNDS(point, CUR.zp1.n_points))
        {
            if (CUR.pedantic_hinting)
            {
                CUR.error = TT_Err_Invalid_Reference;
                return;
            }
        }
        else
        {
            distance = CUR_Func_project(CUR.zp1.cur + point,
                                        CUR.zp0.cur + CUR.GS.rp0);

            CUR_Func_move(&CUR.zp1, point, -distance);
        }

        CUR.GS.loop--;
    }

    CUR.GS.loop = 1;
    CUR.new_top = CUR.args;
}


/*************************************************************************/
/*                                                                       */
/* ISECT[]:      moves point to InterSECTion                             */
/* Opcode range: 0x0F                                                    */
/* Stack:        5 * uint32 -->                                          */
/*                                                                       */
static void
Ins_ISECT(INS_ARG)
{
    FT_UShort point,
              a0, a1,
              b0, b1;

    FT_F26Dot6 discriminant;

    FT_F26Dot6 dx, dy,
               dax, day,
               dbx, dby;

    FT_F26Dot6 val;

    FT_Vector R;


    point = (FT_UShort)args[0];

    a0 = (FT_UShort)args[1];
    a1 = (FT_UShort)args[2];
    b0 = (FT_UShort)args[3];
    b1 = (FT_UShort)args[4];

    if (BOUNDS(b0, CUR.zp0.n_points) ||
        BOUNDS(b1, CUR.zp0.n_points) ||
        BOUNDS(a0, CUR.zp1.n_points) ||
        BOUNDS(a1, CUR.zp1.n_points) ||
        BOUNDS(point, CUR.zp2.n_points))
    {
        if (CUR.pedantic_hinting)
            CUR.error = TT_Err_Invalid_Reference;
        return;
    }

    dbx = CUR.zp0.cur[b1].x - CUR.zp0.cur[b0].x;
    dby = CUR.zp0.cur[b1].y - CUR.zp0.cur[b0].y;

    dax = CUR.zp1.cur[a1].x - CUR.zp1.cur[a0].x;
    day = CUR.zp1.cur[a1].y - CUR.zp1.cur[a0].y;

    dx = CUR.zp0.cur[b0].x - CUR.zp1.cur[a0].x;
    dy = CUR.zp0.cur[b0].y - CUR.zp1.cur[a0].y;

    CUR.zp2.tags[point] |= FT_CURVE_TAG_TOUCH_BOTH;

    discriminant = TT_MULDIV(dax, -dby, 0x40) +
                   TT_MULDIV(day, dbx, 0x40);

    if (FT_ABS(discriminant) >= 0x40)
    {
        val = TT_MULDIV(dx, -dby, 0x40) + TT_MULDIV(dy, dbx, 0x40);

        R.x = TT_MULDIV(val, dax, discriminant);
        R.y = TT_MULDIV(val, day, discriminant);

        CUR.zp2.cur[point].x = CUR.zp1.cur[a0].x + R.x;
        CUR.zp2.cur[point].y = CUR.zp1.cur[a0].y + R.y;
    }
    else
    {
        /* else, take the middle of the middles of A and B */

        CUR.zp2.cur[point].x = (CUR.zp1.cur[a0].x +
                                CUR.zp1.cur[a1].x +
                                CUR.zp0.cur[b0].x +
                                CUR.zp0.cur[b1].x) / 4;
        CUR.zp2.cur[point].y = (CUR.zp1.cur[a0].y +
                                CUR.zp1.cur[a1].y +
                                CUR.zp0.cur[b0].y +
                                CUR.zp0.cur[b1].y) / 4;
    }
}


/*************************************************************************/
/*                                                                       */
/* ALIGNPTS[]:   ALIGN PoinTS                                            */
/* Opcode range: 0x27                                                    */
/* Stack:        uint32 uint32 -->                                       */
/*                                                                       */
static void
Ins_ALIGNPTS(INS_ARG)
{
    FT_UShort p1, p2;
    FT_F26Dot6 distance;


    p1 = (FT_UShort)args[0];
    p2 = (FT_UShort)args[1];

    if (BOUNDS(args[0], CUR.zp1.n_points) ||
        BOUNDS(args[1], CUR.zp0.n_points))
    {
        if (CUR.pedantic_hinting)
            CUR.error = TT_Err_Invalid_Reference;
        return;
    }

    distance = CUR_Func_project(CUR.zp0.cur + p2,
                                CUR.zp1.cur + p1) / 2;

    CUR_Func_move(&CUR.zp1, p1, distance);
    CUR_Func_move(&CUR.zp0, p2, -distance);
}


/*************************************************************************/
/*                                                                       */
/* IP[]:         Interpolate Point                                       */
/* Opcode range: 0x39                                                    */
/* Stack:        uint32... -->                                           */
/*                                                                       */

/* SOMETIMES, DUMBER CODE IS BETTER CODE */

static void
Ins_IP(INS_ARG)
{
    FT_F26Dot6 old_range, cur_range;
    FT_Vector* orus_base;
    FT_Vector* cur_base;
    FT_Int twilight;

    FT_UNUSED_ARG;


    if (CUR.top < CUR.GS.loop)
    {
        CUR.error = TT_Err_Invalid_Reference;
        return;
    }

    /*
     * We need to deal in a special way with the twilight zone.
     * Otherwise, by definition, the value of CUR.twilight.orus[n] is (0,0),
     * for every n.
     */
    twilight = CUR.GS.gep0 == 0 || CUR.GS.gep1 == 0 || CUR.GS.gep2 == 0;

    if (BOUNDS(CUR.GS.rp1, CUR.zp0.n_points))
    {
        if (CUR.pedantic_hinting)
            CUR.error = TT_Err_Invalid_Reference;
        return;
    }

    if (twilight)
        orus_base = &CUR.zp0.org[CUR.GS.rp1];
    else
        orus_base = &CUR.zp0.orus[CUR.GS.rp1];

    cur_base = &CUR.zp0.cur[CUR.GS.rp1];

    /* XXX: There are some glyphs in some braindead but popular */
    /*      fonts out there (e.g. [aeu]grave in monotype.ttf)   */
    /*      calling IP[] with bad values of rp[12].             */
    /*      Do something sane when this odd thing happens.      */
    if (BOUNDS(CUR.GS.rp1, CUR.zp0.n_points) ||
        BOUNDS(CUR.GS.rp2, CUR.zp1.n_points))
    {
        old_range = 0;
        cur_range = 0;
    }
    else
    {
        if (twilight)
            old_range = CUR_Func_dualproj(&CUR.zp1.org[CUR.GS.rp2],
                                          orus_base);
        else
            old_range = CUR_Func_dualproj(&CUR.zp1.orus[CUR.GS.rp2],
                                          orus_base);

        cur_range = CUR_Func_project(&CUR.zp1.cur[CUR.GS.rp2], cur_base);
    }

    for (; CUR.GS.loop > 0; --CUR.GS.loop)
    {
        FT_UInt point = (FT_UInt)CUR.stack[--CUR.args];
        FT_F26Dot6 org_dist, cur_dist, new_dist;


        /* check point bounds */
        if (BOUNDS(point, CUR.zp2.n_points))
        {
            if (CUR.pedantic_hinting)
            {
                CUR.error = TT_Err_Invalid_Reference;
                return;
            }
            continue;
        }

        if (twilight)
            org_dist = CUR_Func_dualproj(&CUR.zp2.org[point], orus_base);
        else
            org_dist = CUR_Func_dualproj(&CUR.zp2.orus[point], orus_base);

        cur_dist = CUR_Func_project(&CUR.zp2.cur[point], cur_base);

        if (org_dist)
            new_dist = (old_range != 0)
                       ? TT_MULDIV(org_dist, cur_range, old_range)
                       : cur_dist;
        else
            new_dist = 0;

        CUR_Func_move(&CUR.zp2, (FT_UShort)point, new_dist - cur_dist);
    }
    CUR.GS.loop = 1;
    CUR.new_top = CUR.args;
}


/*************************************************************************/
/*                                                                       */
/* UTP[a]:       UnTouch Point                                           */
/* Opcode range: 0x29                                                    */
/* Stack:        uint32 -->                                              */
/*                                                                       */
static void
Ins_UTP(INS_ARG)
{
    FT_UShort point;
    FT_Byte mask;


    point = (FT_UShort)args[0];

    if (BOUNDS(point, CUR.zp0.n_points))
    {
        if (CUR.pedantic_hinting)
            CUR.error = TT_Err_Invalid_Reference;
        return;
    }

    mask = 0xFF;

    if (CUR.GS.freeVector.x != 0)
        mask &= ~FT_CURVE_TAG_TOUCH_X;

    if (CUR.GS.freeVector.y != 0)
        mask &= ~FT_CURVE_TAG_TOUCH_Y;

    CUR.zp0.tags[point] &= mask;
}


/* Local variables for Ins_IUP: */
typedef struct  IUP_WorkerRec_
{
    FT_Vector* orgs;    /* original and current coordinate */
    FT_Vector* curs;    /* arrays                          */
    FT_Vector* orus;
    FT_UInt max_points;

} IUP_WorkerRec, * IUP_Worker;


static void
_iup_worker_shift(IUP_Worker worker,
                  FT_UInt p1,
                  FT_UInt p2,
                  FT_UInt p)
{
    FT_UInt i;
    FT_F26Dot6 dx;


    dx = worker->curs[p].x - worker->orgs[p].x;
    if (dx != 0)
    {
        for (i = p1; i < p; i++)
            worker->curs[i].x += dx;

        for (i = p + 1; i <= p2; i++)
            worker->curs[i].x += dx;
    }
}


static void
_iup_worker_interpolate(IUP_Worker worker,
                        FT_UInt p1,
                        FT_UInt p2,
                        FT_UInt ref1,
                        FT_UInt ref2)
{
    FT_UInt i;
    FT_F26Dot6 orus1, orus2, org1, org2, delta1, delta2;


    if (p1 > p2)
        return;

    if (BOUNDS(ref1, worker->max_points) ||
        BOUNDS(ref2, worker->max_points))
        return;

    orus1 = worker->orus[ref1].x;
    orus2 = worker->orus[ref2].x;

    if (orus1 > orus2)
    {
        FT_F26Dot6 tmp_o;
        FT_UInt tmp_r;


        tmp_o = orus1;
        orus1 = orus2;
        orus2 = tmp_o;

        tmp_r = ref1;
        ref1 = ref2;
        ref2 = tmp_r;
    }

    org1 = worker->orgs[ref1].x;
    org2 = worker->orgs[ref2].x;
    delta1 = worker->curs[ref1].x - org1;
    delta2 = worker->curs[ref2].x - org2;

    if (orus1 == orus2)
    {
        /* simple shift of untouched points */
        for (i = p1; i <= p2; i++)
        {
            FT_F26Dot6 x = worker->orgs[i].x;


            if (x <= org1)
                x += delta1;
            else
                x += delta2;

            worker->curs[i].x = x;
        }
    }
    else
    {
        FT_Fixed scale = 0;
        FT_Bool scale_valid = 0;


        /* interpolation */
        for (i = p1; i <= p2; i++)
        {
            FT_F26Dot6 x = worker->orgs[i].x;


            if (x <= org1)
                x += delta1;

            else if (x >= org2)
                x += delta2;

            else
            {
                if (!scale_valid)
                {
                    scale_valid = 1;
                    scale = TT_MULDIV(org2 + delta2 - (org1 + delta1),
                                      0x10000L, orus2 - orus1);
                }

                x = (org1 + delta1) +
                    TT_MULFIX(worker->orus[i].x - orus1, scale);
            }
            worker->curs[i].x = x;
        }
    }
}


/*************************************************************************/
/*                                                                       */
/* IUP[a]:       Interpolate Untouched Points                            */
/* Opcode range: 0x30-0x31                                               */
/* Stack:        -->                                                     */
/*                                                                       */
static void
Ins_IUP(INS_ARG)
{
    IUP_WorkerRec V;
    FT_Byte mask;

    FT_UInt first_point;     /* first point of contour        */
    FT_UInt end_point;       /* end point (last+1) of contour */

    FT_UInt first_touched;   /* first touched point in contour   */
    FT_UInt cur_touched;     /* current touched point in contour */

    FT_UInt point;           /* current point   */
    FT_Short contour;        /* current contour */

    FT_UNUSED_ARG;


    /* ignore empty outlines */
    if (CUR.pts.n_contours == 0)
        return;

    if (CUR.opcode & 1)
    {
        mask = FT_CURVE_TAG_TOUCH_X;
        V.orgs = CUR.pts.org;
        V.curs = CUR.pts.cur;
        V.orus = CUR.pts.orus;
    }
    else
    {
        mask = FT_CURVE_TAG_TOUCH_Y;
        V.orgs = (FT_Vector*)((FT_Pos*)CUR.pts.org + 1);
        V.curs = (FT_Vector*)((FT_Pos*)CUR.pts.cur + 1);
        V.orus = (FT_Vector*)((FT_Pos*)CUR.pts.orus + 1);
    }
    V.max_points = CUR.pts.n_points;

    contour = 0;
    point = 0;

    do
    {
        end_point = CUR.pts.contours[contour] - CUR.pts.first_point;
        first_point = point;

        if (CUR.pts.n_points <= end_point)
            end_point = CUR.pts.n_points;

        while (point <= end_point && (CUR.pts.tags[point] & mask) == 0)
            point++;

        if (point <= end_point)
        {
            first_touched = point;
            cur_touched = point;

            point++;

            while (point <= end_point)
            {
                if ((CUR.pts.tags[point] & mask) != 0)
                {
                    if (point > 0)
                        _iup_worker_interpolate(&V,
                                                cur_touched + 1,
                                                point - 1,
                                                cur_touched,
                                                point);
                    cur_touched = point;
                }

                point++;
            }

            if (cur_touched == first_touched)
                _iup_worker_shift(&V, first_point, end_point, cur_touched);
            else
            {
                _iup_worker_interpolate(&V,
                                        (FT_UShort)(cur_touched + 1),
                                        end_point,
                                        cur_touched,
                                        first_touched);

                if (first_touched > 0)
                    _iup_worker_interpolate(&V,
                                            first_point,
                                            first_touched - 1,
                                            cur_touched,
                                            first_touched);
            }
        }
        contour++;
    }
    while (contour < CUR.pts.n_contours);
}


/*************************************************************************/
/*                                                                       */
/* DELTAPn[]:    DELTA exceptions P1, P2, P3                             */
/* Opcode range: 0x5D,0x71,0x72                                          */
/* Stack:        uint32 (2 * uint32)... -->                              */
/*                                                                       */
static void
Ins_DELTAP(INS_ARG)
{
    FT_ULong k, nump;
    FT_UShort A;
    FT_ULong C;
    FT_Long B;


    #ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
    /* Delta hinting is covered by US Patent 5159668. */
    if (CUR.face->unpatented_hinting)
    {
        FT_Long n = args[0] * 2;


        if (CUR.args < n)
        {
            CUR.error = TT_Err_Too_Few_Arguments;
            return;
        }

        CUR.args -= n;
        CUR.new_top = CUR.args;
        return;
    }
    #endif

    nump = (FT_ULong)args[0];   /* some points theoretically may occur more
                                   than once, thus UShort isn't enough */

    for (k = 1; k <= nump; k++)
    {
        if (CUR.args < 2)
        {
            CUR.error = TT_Err_Too_Few_Arguments;
            return;
        }

        CUR.args -= 2;

        A = (FT_UShort)CUR.stack[CUR.args + 1];
        B = CUR.stack[CUR.args];

        /* XXX: Because some popular fonts contain some invalid DeltaP */
        /*      instructions, we simply ignore them when the stacked   */
        /*      point reference is off limit, rather than returning an */
        /*      error.  As a delta instruction doesn't change a glyph  */
        /*      in great ways, this shouldn't be a problem.            */

        if (!BOUNDS(A, CUR.zp0.n_points))
        {
            C = ((FT_ULong)B & 0xF0) >> 4;

            switch (CUR.opcode)
            {
                case 0x5D:
                    break;

                case 0x71:
                    C += 16;
                    break;

                case 0x72:
                    C += 32;
                    break;
            }

            C += CUR.GS.delta_base;

            if (CURRENT_Ppem() == (FT_Long)C)
            {
                B = ((FT_ULong)B & 0xF) - 8;
                if (B >= 0)
                    B++;
                B = B * 64 / (1L << CUR.GS.delta_shift);

                CUR_Func_move(&CUR.zp0, A, B);
            }
        }
        else
        if (CUR.pedantic_hinting)
            CUR.error = TT_Err_Invalid_Reference;
    }

    CUR.new_top = CUR.args;
}


/*************************************************************************/
/*                                                                       */
/* DELTACn[]:    DELTA exceptions C1, C2, C3                             */
/* Opcode range: 0x73,0x74,0x75                                          */
/* Stack:        uint32 (2 * uint32)... -->                              */
/*                                                                       */
static void
Ins_DELTAC(INS_ARG)
{
    FT_ULong nump, k;
    FT_ULong A, C;
    FT_Long B;


    #ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
    /* Delta hinting is covered by US Patent 5159668. */
    if (CUR.face->unpatented_hinting)
    {
        FT_Long n = args[0] * 2;


        if (CUR.args < n)
        {
            CUR.error = TT_Err_Too_Few_Arguments;
            return;
        }

        CUR.args -= n;
        CUR.new_top = CUR.args;
        return;
    }
    #endif

    nump = (FT_ULong)args[0];

    for (k = 1; k <= nump; k++)
    {
        if (CUR.args < 2)
        {
            CUR.error = TT_Err_Too_Few_Arguments;
            return;
        }

        CUR.args -= 2;

        A = (FT_ULong)CUR.stack[CUR.args + 1];
        B = CUR.stack[CUR.args];

        if (BOUNDS(A, CUR.cvtSize))
        {
            if (CUR.pedantic_hinting)
            {
                CUR.error = TT_Err_Invalid_Reference;
                return;
            }
        }
        else
        {
            C = ((FT_ULong)B & 0xF0) >> 4;

            switch (CUR.opcode)
            {
                case 0x73:
                    break;

                case 0x74:
                    C += 16;
                    break;

                case 0x75:
                    C += 32;
                    break;
            }

            C += CUR.GS.delta_base;

            if (CURRENT_Ppem() == (FT_Long)C)
            {
                B = ((FT_ULong)B & 0xF) - 8;
                if (B >= 0)
                    B++;
                B = B * 64 / (1L << CUR.GS.delta_shift);

                CUR_Func_move_cvt(A, B);
            }
        }
    }

    CUR.new_top = CUR.args;
}


/*************************************************************************/
/*                                                                       */
/* MISC. INSTRUCTIONS                                                    */
/*                                                                       */
/*************************************************************************/


/*************************************************************************/
/*                                                                       */
/* GETINFO[]:    GET INFOrmation                                         */
/* Opcode range: 0x88                                                    */
/* Stack:        uint32 --> uint32                                       */
/*                                                                       */
static void
Ins_GETINFO(INS_ARG)
{
    FT_Long K;


    K = 0;

    /* We return MS rasterizer version 1.7 for the font scaler. */
    if ((args[0] & 1) != 0)
        K = 35;

    /* Has the glyph been rotated? */
    if ((args[0] & 2) != 0 && CUR.tt_metrics.rotated)
        K |= 0x80;

    /* Has the glyph been stretched? */
    if ((args[0] & 4) != 0 && CUR.tt_metrics.stretched)
        K |= 1 << 8;

    /* Are we hinting for grayscale? */
    if ((args[0] & 32) != 0 && CUR.grayscale)
        K |= 1 << 12;

    args[0] = K;
}


static void
Ins_UNKNOWN(INS_ARG)
{
    TT_DefRecord* def = CUR.IDefs;
    TT_DefRecord* limit = def + CUR.numIDefs;

    FT_UNUSED_ARG;


    for (; def < limit; def++)
    {
        if ((FT_Byte)def->opc == CUR.opcode && def->active)
        {
            TT_CallRec* call;


            if (CUR.callTop >= CUR.callSize)
            {
                CUR.error = TT_Err_Stack_Overflow;
                return;
            }

            call = CUR.callStack + CUR.callTop++;

            call->Caller_Range = CUR.curRange;
            call->Caller_IP = CUR.IP + 1;
            call->Cur_Count = 1;
            call->Cur_Restart = def->start;

            INS_Goto_CodeRange(def->range, def->start);

            CUR.step_ins = FALSE;
            return;
        }
    }

    CUR.error = TT_Err_Invalid_Opcode;
}


    #ifndef TT_CONFIG_OPTION_INTERPRETER_SWITCH


static
TInstruction_Function Instruct_Dispatch[256] =
{
    /* Opcodes are gathered in groups of 16. */
    /* Please keep the spaces as they are.   */

    /*  SVTCA  y  */ Ins_SVTCA,
    /*  SVTCA  x  */ Ins_SVTCA,
    /*  SPvTCA y  */ Ins_SPVTCA,
    /*  SPvTCA x  */ Ins_SPVTCA,
    /*  SFvTCA y  */ Ins_SFVTCA,
    /*  SFvTCA x  */ Ins_SFVTCA,
    /*  SPvTL //  */ Ins_SPVTL,
    /*  SPvTL +   */ Ins_SPVTL,
    /*  SFvTL //  */ Ins_SFVTL,
    /*  SFvTL +   */ Ins_SFVTL,
    /*  SPvFS     */ Ins_SPVFS,
    /*  SFvFS     */ Ins_SFVFS,
    /*  GPV       */ Ins_GPV,
    /*  GFV       */ Ins_GFV,
    /*  SFvTPv    */ Ins_SFVTPV,
    /*  ISECT     */ Ins_ISECT,

    /*  SRP0      */ Ins_SRP0,
    /*  SRP1      */ Ins_SRP1,
    /*  SRP2      */ Ins_SRP2,
    /*  SZP0      */ Ins_SZP0,
    /*  SZP1      */ Ins_SZP1,
    /*  SZP2      */ Ins_SZP2,
    /*  SZPS      */ Ins_SZPS,
    /*  SLOOP     */ Ins_SLOOP,
    /*  RTG       */ Ins_RTG,
    /*  RTHG      */ Ins_RTHG,
    /*  SMD       */ Ins_SMD,
    /*  ELSE      */ Ins_ELSE,
    /*  JMPR      */ Ins_JMPR,
    /*  SCvTCi    */ Ins_SCVTCI,
    /*  SSwCi     */ Ins_SSWCI,
    /*  SSW       */ Ins_SSW,

    /*  DUP       */ Ins_DUP,
    /*  POP       */ Ins_POP,
    /*  CLEAR     */ Ins_CLEAR,
    /*  SWAP      */ Ins_SWAP,
    /*  DEPTH     */ Ins_DEPTH,
    /*  CINDEX    */ Ins_CINDEX,
    /*  MINDEX    */ Ins_MINDEX,
    /*  AlignPTS  */ Ins_ALIGNPTS,
    /*  INS_0x28  */ Ins_UNKNOWN,
    /*  UTP       */ Ins_UTP,
    /*  LOOPCALL  */ Ins_LOOPCALL,
    /*  CALL      */ Ins_CALL,
    /*  FDEF      */ Ins_FDEF,
    /*  ENDF      */ Ins_ENDF,
    /*  MDAP[0]   */ Ins_MDAP,
    /*  MDAP[1]   */ Ins_MDAP,

    /*  IUP[0]    */ Ins_IUP,
    /*  IUP[1]    */ Ins_IUP,
    /*  SHP[0]    */ Ins_SHP,
    /*  SHP[1]    */ Ins_SHP,
    /*  SHC[0]    */ Ins_SHC,
    /*  SHC[1]    */ Ins_SHC,
    /*  SHZ[0]    */ Ins_SHZ,
    /*  SHZ[1]    */ Ins_SHZ,
    /*  SHPIX     */ Ins_SHPIX,
    /*  IP        */ Ins_IP,
    /*  MSIRP[0]  */ Ins_MSIRP,
    /*  MSIRP[1]  */ Ins_MSIRP,
    /*  AlignRP   */ Ins_ALIGNRP,
    /*  RTDG      */ Ins_RTDG,
    /*  MIAP[0]   */ Ins_MIAP,
    /*  MIAP[1]   */ Ins_MIAP,

    /*  NPushB    */ Ins_NPUSHB,
    /*  NPushW    */ Ins_NPUSHW,
    /*  WS        */ Ins_WS,
    /*  RS        */ Ins_RS,
    /*  WCvtP     */ Ins_WCVTP,
    /*  RCvt      */ Ins_RCVT,
    /*  GC[0]     */ Ins_GC,
    /*  GC[1]     */ Ins_GC,
    /*  SCFS      */ Ins_SCFS,
    /*  MD[0]     */ Ins_MD,
    /*  MD[1]     */ Ins_MD,
    /*  MPPEM     */ Ins_MPPEM,
    /*  MPS       */ Ins_MPS,
    /*  FlipON    */ Ins_FLIPON,
    /*  FlipOFF   */ Ins_FLIPOFF,
    /*  DEBUG     */ Ins_DEBUG,

    /*  LT        */ Ins_LT,
    /*  LTEQ      */ Ins_LTEQ,
    /*  GT        */ Ins_GT,
    /*  GTEQ      */ Ins_GTEQ,
    /*  EQ        */ Ins_EQ,
    /*  NEQ       */ Ins_NEQ,
    /*  ODD       */ Ins_ODD,
    /*  EVEN      */ Ins_EVEN,
    /*  IF        */ Ins_IF,
    /*  EIF       */ Ins_EIF,
    /*  AND       */ Ins_AND,
    /*  OR        */ Ins_OR,
    /*  NOT       */ Ins_NOT,
    /*  DeltaP1   */ Ins_DELTAP,
    /*  SDB       */ Ins_SDB,
    /*  SDS       */ Ins_SDS,

    /*  ADD       */ Ins_ADD,
    /*  SUB       */ Ins_SUB,
    /*  DIV       */ Ins_DIV,
    /*  MUL       */ Ins_MUL,
    /*  ABS       */ Ins_ABS,
    /*  NEG       */ Ins_NEG,
    /*  FLOOR     */ Ins_FLOOR,
    /*  CEILING   */ Ins_CEILING,
    /*  ROUND[0]  */ Ins_ROUND,
    /*  ROUND[1]  */ Ins_ROUND,
    /*  ROUND[2]  */ Ins_ROUND,
    /*  ROUND[3]  */ Ins_ROUND,
    /*  NROUND[0] */ Ins_NROUND,
    /*  NROUND[1] */ Ins_NROUND,
    /*  NROUND[2] */ Ins_NROUND,
    /*  NROUND[3] */ Ins_NROUND,

    /*  WCvtF     */ Ins_WCVTF,
    /*  DeltaP2   */ Ins_DELTAP,
    /*  DeltaP3   */ Ins_DELTAP,
    /*  DeltaCn[0] */ Ins_DELTAC,
    /*  DeltaCn[1] */ Ins_DELTAC,
    /*  DeltaCn[2] */ Ins_DELTAC,
    /*  SROUND    */ Ins_SROUND,
    /*  S45Round  */ Ins_S45ROUND,
    /*  JROT      */ Ins_JROT,
    /*  JROF      */ Ins_JROF,
    /*  ROFF      */ Ins_ROFF,
    /*  INS_0x7B  */ Ins_UNKNOWN,
    /*  RUTG      */ Ins_RUTG,
    /*  RDTG      */ Ins_RDTG,
    /*  SANGW     */ Ins_SANGW,
    /*  AA        */ Ins_AA,

    /*  FlipPT    */ Ins_FLIPPT,
    /*  FlipRgON  */ Ins_FLIPRGON,
    /*  FlipRgOFF */ Ins_FLIPRGOFF,
    /*  INS_0x83  */ Ins_UNKNOWN,
    /*  INS_0x84  */ Ins_UNKNOWN,
    /*  ScanCTRL  */ Ins_SCANCTRL,
    /*  SDPVTL[0] */ Ins_SDPVTL,
    /*  SDPVTL[1] */ Ins_SDPVTL,
    /*  GetINFO   */ Ins_GETINFO,
    /*  IDEF      */ Ins_IDEF,
    /*  ROLL      */ Ins_ROLL,
    /*  MAX       */ Ins_MAX,
    /*  MIN       */ Ins_MIN,
    /*  ScanTYPE  */ Ins_SCANTYPE,
    /*  InstCTRL  */ Ins_INSTCTRL,
    /*  INS_0x8F  */ Ins_UNKNOWN,

    /*  INS_0x90  */ Ins_UNKNOWN,
    /*  INS_0x91  */ Ins_UNKNOWN,
    /*  INS_0x92  */ Ins_UNKNOWN,
    /*  INS_0x93  */ Ins_UNKNOWN,
    /*  INS_0x94  */ Ins_UNKNOWN,
    /*  INS_0x95  */ Ins_UNKNOWN,
    /*  INS_0x96  */ Ins_UNKNOWN,
    /*  INS_0x97  */ Ins_UNKNOWN,
    /*  INS_0x98  */ Ins_UNKNOWN,
    /*  INS_0x99  */ Ins_UNKNOWN,
    /*  INS_0x9A  */ Ins_UNKNOWN,
    /*  INS_0x9B  */ Ins_UNKNOWN,
    /*  INS_0x9C  */ Ins_UNKNOWN,
    /*  INS_0x9D  */ Ins_UNKNOWN,
    /*  INS_0x9E  */ Ins_UNKNOWN,
    /*  INS_0x9F  */ Ins_UNKNOWN,

    /*  INS_0xA0  */ Ins_UNKNOWN,
    /*  INS_0xA1  */ Ins_UNKNOWN,
    /*  INS_0xA2  */ Ins_UNKNOWN,
    /*  INS_0xA3  */ Ins_UNKNOWN,
    /*  INS_0xA4  */ Ins_UNKNOWN,
    /*  INS_0xA5  */ Ins_UNKNOWN,
    /*  INS_0xA6  */ Ins_UNKNOWN,
    /*  INS_0xA7  */ Ins_UNKNOWN,
    /*  INS_0xA8  */ Ins_UNKNOWN,
    /*  INS_0xA9  */ Ins_UNKNOWN,
    /*  INS_0xAA  */ Ins_UNKNOWN,
    /*  INS_0xAB  */ Ins_UNKNOWN,
    /*  INS_0xAC  */ Ins_UNKNOWN,
    /*  INS_0xAD  */ Ins_UNKNOWN,
    /*  INS_0xAE  */ Ins_UNKNOWN,
    /*  INS_0xAF  */ Ins_UNKNOWN,

    /*  PushB[0]  */ Ins_PUSHB,
    /*  PushB[1]  */ Ins_PUSHB,
    /*  PushB[2]  */ Ins_PUSHB,
    /*  PushB[3]  */ Ins_PUSHB,
    /*  PushB[4]  */ Ins_PUSHB,
    /*  PushB[5]  */ Ins_PUSHB,
    /*  PushB[6]  */ Ins_PUSHB,
    /*  PushB[7]  */ Ins_PUSHB,
    /*  PushW[0]  */ Ins_PUSHW,
    /*  PushW[1]  */ Ins_PUSHW,
    /*  PushW[2]  */ Ins_PUSHW,
    /*  PushW[3]  */ Ins_PUSHW,
    /*  PushW[4]  */ Ins_PUSHW,
    /*  PushW[5]  */ Ins_PUSHW,
    /*  PushW[6]  */ Ins_PUSHW,
    /*  PushW[7]  */ Ins_PUSHW,

    /*  MDRP[00]  */ Ins_MDRP,
    /*  MDRP[01]  */ Ins_MDRP,
    /*  MDRP[02]  */ Ins_MDRP,
    /*  MDRP[03]  */ Ins_MDRP,
    /*  MDRP[04]  */ Ins_MDRP,
    /*  MDRP[05]  */ Ins_MDRP,
    /*  MDRP[06]  */ Ins_MDRP,
    /*  MDRP[07]  */ Ins_MDRP,
    /*  MDRP[08]  */ Ins_MDRP,
    /*  MDRP[09]  */ Ins_MDRP,
    /*  MDRP[10]  */ Ins_MDRP,
    /*  MDRP[11]  */ Ins_MDRP,
    /*  MDRP[12]  */ Ins_MDRP,
    /*  MDRP[13]  */ Ins_MDRP,
    /*  MDRP[14]  */ Ins_MDRP,
    /*  MDRP[15]  */ Ins_MDRP,

    /*  MDRP[16]  */ Ins_MDRP,
    /*  MDRP[17]  */ Ins_MDRP,
    /*  MDRP[18]  */ Ins_MDRP,
    /*  MDRP[19]  */ Ins_MDRP,
    /*  MDRP[20]  */ Ins_MDRP,
    /*  MDRP[21]  */ Ins_MDRP,
    /*  MDRP[22]  */ Ins_MDRP,
    /*  MDRP[23]  */ Ins_MDRP,
    /*  MDRP[24]  */ Ins_MDRP,
    /*  MDRP[25]  */ Ins_MDRP,
    /*  MDRP[26]  */ Ins_MDRP,
    /*  MDRP[27]  */ Ins_MDRP,
    /*  MDRP[28]  */ Ins_MDRP,
    /*  MDRP[29]  */ Ins_MDRP,
    /*  MDRP[30]  */ Ins_MDRP,
    /*  MDRP[31]  */ Ins_MDRP,

    /*  MIRP[00]  */ Ins_MIRP,
    /*  MIRP[01]  */ Ins_MIRP,
    /*  MIRP[02]  */ Ins_MIRP,
    /*  MIRP[03]  */ Ins_MIRP,
    /*  MIRP[04]  */ Ins_MIRP,
    /*  MIRP[05]  */ Ins_MIRP,
    /*  MIRP[06]  */ Ins_MIRP,
    /*  MIRP[07]  */ Ins_MIRP,
    /*  MIRP[08]  */ Ins_MIRP,
    /*  MIRP[09]  */ Ins_MIRP,
    /*  MIRP[10]  */ Ins_MIRP,
    /*  MIRP[11]  */ Ins_MIRP,
    /*  MIRP[12]  */ Ins_MIRP,
    /*  MIRP[13]  */ Ins_MIRP,
    /*  MIRP[14]  */ Ins_MIRP,
    /*  MIRP[15]  */ Ins_MIRP,

    /*  MIRP[16]  */ Ins_MIRP,
    /*  MIRP[17]  */ Ins_MIRP,
    /*  MIRP[18]  */ Ins_MIRP,
    /*  MIRP[19]  */ Ins_MIRP,
    /*  MIRP[20]  */ Ins_MIRP,
    /*  MIRP[21]  */ Ins_MIRP,
    /*  MIRP[22]  */ Ins_MIRP,
    /*  MIRP[23]  */ Ins_MIRP,
    /*  MIRP[24]  */ Ins_MIRP,
    /*  MIRP[25]  */ Ins_MIRP,
    /*  MIRP[26]  */ Ins_MIRP,
    /*  MIRP[27]  */ Ins_MIRP,
    /*  MIRP[28]  */ Ins_MIRP,
    /*  MIRP[29]  */ Ins_MIRP,
    /*  MIRP[30]  */ Ins_MIRP,
    /*  MIRP[31]  */ Ins_MIRP
};


    #endif /* !TT_CONFIG_OPTION_INTERPRETER_SWITCH */


/*************************************************************************/
/*                                                                       */
/* RUN                                                                   */
/*                                                                       */
/*  This function executes a run of opcodes.  It will exit in the        */
/*  following cases:                                                     */
/*                                                                       */
/*  - Errors (in which case it returns FALSE).                           */
/*                                                                       */
/*  - Reaching the end of the main code range (returns TRUE).            */
/*    Reaching the end of a code range within a function call is an      */
/*    error.                                                             */
/*                                                                       */
/*  - After executing one single opcode, if the flag `Instruction_Trap'  */
/*    is set to TRUE (returns TRUE).                                     */
/*                                                                       */
/*  On exit with TRUE, test IP < CodeSize to know whether it comes from  */
/*  an instruction trap or a normal termination.                         */
/*                                                                       */
/*                                                                       */
/*  Note: The documented DEBUG opcode pops a value from the stack.  This */
/*        behaviour is unsupported; here a DEBUG opcode is always an     */
/*        error.                                                         */
/*                                                                       */
/*                                                                       */
/* THIS IS THE INTERPRETER'S MAIN LOOP.                                  */
/*                                                                       */
/*  Instructions appear in the specification's order.                    */
/*                                                                       */
/*************************************************************************/


/* documentation is in ttinterp.h */

FT_EXPORT_DEF(FT_Error)
TT_RunIns(TT_ExecContext exc)
{
    FT_Long ins_counter = 0;   /* executed instructions counter */


    #ifdef TT_CONFIG_OPTION_STATIC_RASTER
    cur = *exc;
    #endif

    /* set CVT functions */
    CUR.tt_metrics.ratio = 0;
    if (CUR.metrics.x_ppem != CUR.metrics.y_ppem)
    {
        /* non-square pixels, use the stretched routines */
        CUR.func_read_cvt = Read_CVT_Stretched;
        CUR.func_write_cvt = Write_CVT_Stretched;
        CUR.func_move_cvt = Move_CVT_Stretched;
    }
    else
    {
        /* square pixels, use normal routines */
        CUR.func_read_cvt = Read_CVT;
        CUR.func_write_cvt = Write_CVT;
        CUR.func_move_cvt = Move_CVT;
    }

    COMPUTE_Funcs();
    COMPUTE_Round((FT_Byte)exc->GS.round_state);

    do
    {
        CUR.opcode = CUR.code[CUR.IP];

        if ((CUR.length = opcode_length[CUR.opcode]) < 0)
        {
            if (CUR.IP + 1 > CUR.codeSize)
                goto LErrorCodeOverflow_;

            CUR.length = 2 - CUR.length * CUR.code[CUR.IP + 1];
        }

        if (CUR.IP + CUR.length > CUR.codeSize)
            goto LErrorCodeOverflow_;

        /* First, let's check for empty stack and overflow */
        CUR.args = CUR.top - (Pop_Push_Count[CUR.opcode] >> 4);

        /* `args' is the top of the stack once arguments have been popped. */
        /* One can also interpret it as the index of the last argument.    */
        if (CUR.args < 0)
        {
            CUR.error = TT_Err_Too_Few_Arguments;
            goto LErrorLabel_;
        }

        CUR.new_top = CUR.args + (Pop_Push_Count[CUR.opcode] & 15);

        /* `new_top' is the new top of the stack, after the instruction's */
        /* execution.  `top' will be set to `new_top' after the `switch'  */
        /* statement.                                                     */
        if (CUR.new_top > CUR.stackSize)
        {
            CUR.error = TT_Err_Stack_Overflow;
            goto LErrorLabel_;
        }

        CUR.step_ins = TRUE;
        CUR.error = TT_Err_Ok;

        #ifdef TT_CONFIG_OPTION_INTERPRETER_SWITCH

        {
            FT_Long* args = CUR.stack + CUR.args;
            FT_Byte opcode = CUR.opcode;


            #undef  ARRAY_BOUND_ERROR
        #define ARRAY_BOUND_ERROR goto Set_Invalid_Ref


            switch (opcode)
            {
                case 0x00: /* SVTCA y  */
                case 0x01: /* SVTCA x  */
                case 0x02: /* SPvTCA y */
                case 0x03: /* SPvTCA x */
                case 0x04: /* SFvTCA y */
                case 0x05: /* SFvTCA x */
                {
                    FT_Short AA, BB;


                    AA = (FT_Short)((opcode & 1) << 14);
                    BB = (FT_Short)(AA ^ 0x4000);

                    if (opcode < 4)
                    {
                        CUR.GS.projVector.x = AA;
                        CUR.GS.projVector.y = BB;

                        CUR.GS.dualVector.x = AA;
                        CUR.GS.dualVector.y = BB;
                    }
                    else
                    {
                        GUESS_VECTOR(projVector);
                    }

                    if ((opcode & 2) == 0)
                    {
                        CUR.GS.freeVector.x = AA;
                        CUR.GS.freeVector.y = BB;
                    }
                    else
                    {
                        GUESS_VECTOR(freeVector);
                    }

                    COMPUTE_Funcs();
                }
                break;

                case 0x06: /* SPvTL // */
                case 0x07: /* SPvTL +  */
                    DO_SPVTL
                    break;

                case 0x08: /* SFvTL // */
                case 0x09: /* SFvTL +  */
                    DO_SFVTL
                    break;

                case 0x0A: /* SPvFS */
                    DO_SPVFS
                    break;

                case 0x0B: /* SFvFS */
                    DO_SFVFS
                    break;

                case 0x0C: /* GPV */
                    DO_GPV
                    break;

                case 0x0D: /* GFV */
                    DO_GFV
                    break;

                case 0x0E: /* SFvTPv */
                    DO_SFVTPV
                    break;

                case 0x0F: /* ISECT  */
                    Ins_ISECT(EXEC_ARG_ args);
                    break;

                case 0x10: /* SRP0 */
                    DO_SRP0
                    break;

                case 0x11: /* SRP1 */
                    DO_SRP1
                    break;

                case 0x12: /* SRP2 */
                    DO_SRP2
                    break;

                case 0x13: /* SZP0 */
                    Ins_SZP0(EXEC_ARG_ args);
                    break;

                case 0x14: /* SZP1 */
                    Ins_SZP1(EXEC_ARG_ args);
                    break;

                case 0x15: /* SZP2 */
                    Ins_SZP2(EXEC_ARG_ args);
                    break;

                case 0x16: /* SZPS */
                    Ins_SZPS(EXEC_ARG_ args);
                    break;

                case 0x17: /* SLOOP */
                    DO_SLOOP
                    break;

                case 0x18: /* RTG */
                    DO_RTG
                    break;

                case 0x19: /* RTHG */
                    DO_RTHG
                    break;

                case 0x1A: /* SMD */
                    DO_SMD
                    break;

                case 0x1B: /* ELSE */
                    Ins_ELSE(EXEC_ARG_ args);
                    break;

                case 0x1C: /* JMPR */
                    DO_JMPR
                    break;

                case 0x1D: /* SCVTCI */
                    DO_SCVTCI
                    break;

                case 0x1E: /* SSWCI */
                    DO_SSWCI
                    break;

                case 0x1F: /* SSW */
                    DO_SSW
                    break;

                case 0x20: /* DUP */
                    DO_DUP
                    break;

                case 0x21: /* POP */
                    /* nothing :-) */
                    break;

                case 0x22: /* CLEAR */
                    DO_CLEAR
                    break;

                case 0x23: /* SWAP */
                    DO_SWAP
                    break;

                case 0x24: /* DEPTH */
                    DO_DEPTH
                    break;

                case 0x25: /* CINDEX */
                    DO_CINDEX
                    break;

                case 0x26: /* MINDEX */
                    Ins_MINDEX(EXEC_ARG_ args);
                    break;

                case 0x27: /* ALIGNPTS */
                    Ins_ALIGNPTS(EXEC_ARG_ args);
                    break;

                case 0x28: /* ???? */
                    Ins_UNKNOWN(EXEC_ARG_ args);
                    break;

                case 0x29: /* UTP */
                    Ins_UTP(EXEC_ARG_ args);
                    break;

                case 0x2A: /* LOOPCALL */
                    Ins_LOOPCALL(EXEC_ARG_ args);
                    break;

                case 0x2B: /* CALL */
                    Ins_CALL(EXEC_ARG_ args);
                    break;

                case 0x2C: /* FDEF */
                    Ins_FDEF(EXEC_ARG_ args);
                    break;

                case 0x2D: /* ENDF */
                    Ins_ENDF(EXEC_ARG_ args);
                    break;

                case 0x2E: /* MDAP */
                case 0x2F: /* MDAP */
                    Ins_MDAP(EXEC_ARG_ args);
                    break;


                case 0x30: /* IUP */
                case 0x31: /* IUP */
                    Ins_IUP(EXEC_ARG_ args);
                    break;

                case 0x32: /* SHP */
                case 0x33: /* SHP */
                    Ins_SHP(EXEC_ARG_ args);
                    break;

                case 0x34: /* SHC */
                case 0x35: /* SHC */
                    Ins_SHC(EXEC_ARG_ args);
                    break;

                case 0x36: /* SHZ */
                case 0x37: /* SHZ */
                    Ins_SHZ(EXEC_ARG_ args);
                    break;

                case 0x38: /* SHPIX */
                    Ins_SHPIX(EXEC_ARG_ args);
                    break;

                case 0x39: /* IP    */
                    Ins_IP(EXEC_ARG_ args);
                    break;

                case 0x3A: /* MSIRP */
                case 0x3B: /* MSIRP */
                    Ins_MSIRP(EXEC_ARG_ args);
                    break;

                case 0x3C: /* AlignRP */
                    Ins_ALIGNRP(EXEC_ARG_ args);
                    break;

                case 0x3D: /* RTDG */
                    DO_RTDG
                    break;

                case 0x3E: /* MIAP */
                case 0x3F: /* MIAP */
                    Ins_MIAP(EXEC_ARG_ args);
                    break;

                case 0x40: /* NPUSHB */
                    Ins_NPUSHB(EXEC_ARG_ args);
                    break;

                case 0x41: /* NPUSHW */
                    Ins_NPUSHW(EXEC_ARG_ args);
                    break;

                case 0x42: /* WS */
                    DO_WS
                    break;

Set_Invalid_Ref:
                    CUR.error = TT_Err_Invalid_Reference;
                    break;

                case 0x43: /* RS */
                    DO_RS
                    break;

                case 0x44: /* WCVTP */
                    DO_WCVTP
                    break;

                case 0x45: /* RCVT */
                    DO_RCVT
                    break;

                case 0x46: /* GC */
                case 0x47: /* GC */
                    Ins_GC(EXEC_ARG_ args);
                    break;

                case 0x48: /* SCFS */
                    Ins_SCFS(EXEC_ARG_ args);
                    break;

                case 0x49: /* MD */
                case 0x4A: /* MD */
                    Ins_MD(EXEC_ARG_ args);
                    break;

                case 0x4B: /* MPPEM */
                    DO_MPPEM
                    break;

                case 0x4C: /* MPS */
                    DO_MPS
                    break;

                case 0x4D: /* FLIPON */
                    DO_FLIPON
                    break;

                case 0x4E: /* FLIPOFF */
                    DO_FLIPOFF
                    break;

                case 0x4F: /* DEBUG */
                    DO_DEBUG
                    break;

                case 0x50: /* LT */
                    DO_LT
                    break;

                case 0x51: /* LTEQ */
                    DO_LTEQ
                    break;

                case 0x52: /* GT */
                    DO_GT
                    break;

                case 0x53: /* GTEQ */
                    DO_GTEQ
                    break;

                case 0x54: /* EQ */
                    DO_EQ
                    break;

                case 0x55: /* NEQ */
                    DO_NEQ
                    break;

                case 0x56: /* ODD */
                    DO_ODD
                    break;

                case 0x57: /* EVEN */
                    DO_EVEN
                    break;

                case 0x58: /* IF */
                    Ins_IF(EXEC_ARG_ args);
                    break;

                case 0x59: /* EIF */
                    /* do nothing */
                    break;

                case 0x5A: /* AND */
                    DO_AND
                    break;

                case 0x5B: /* OR */
                    DO_OR
                    break;

                case 0x5C: /* NOT */
                    DO_NOT
                    break;

                case 0x5D: /* DELTAP1 */
                    Ins_DELTAP(EXEC_ARG_ args);
                    break;

                case 0x5E: /* SDB */
                    DO_SDB
                    break;

                case 0x5F: /* SDS */
                    DO_SDS
                    break;

                case 0x60: /* ADD */
                    DO_ADD
                    break;

                case 0x61: /* SUB */
                    DO_SUB
                    break;

                case 0x62: /* DIV */
                    DO_DIV
                    break;

                case 0x63: /* MUL */
                    DO_MUL
                    break;

                case 0x64: /* ABS */
                    DO_ABS
                    break;

                case 0x65: /* NEG */
                    DO_NEG
                    break;

                case 0x66: /* FLOOR */
                    DO_FLOOR
                    break;

                case 0x67: /* CEILING */
                    DO_CEILING
                    break;

                case 0x68: /* ROUND */
                case 0x69: /* ROUND */
                case 0x6A: /* ROUND */
                case 0x6B: /* ROUND */
                    DO_ROUND
                    break;

                case 0x6C: /* NROUND */
                case 0x6D: /* NROUND */
                case 0x6E: /* NRRUND */
                case 0x6F: /* NROUND */
                    DO_NROUND
                    break;

                case 0x70: /* WCVTF */
                    DO_WCVTF
                    break;

                case 0x71: /* DELTAP2 */
                case 0x72: /* DELTAP3 */
                    Ins_DELTAP(EXEC_ARG_ args);
                    break;

                case 0x73: /* DELTAC0 */
                case 0x74: /* DELTAC1 */
                case 0x75: /* DELTAC2 */
                    Ins_DELTAC(EXEC_ARG_ args);
                    break;

                case 0x76: /* SROUND */
                    DO_SROUND
                    break;

                case 0x77: /* S45Round */
                    DO_S45ROUND
                    break;

                case 0x78: /* JROT */
                    DO_JROT
                    break;

                case 0x79: /* JROF */
                    DO_JROF
                    break;

                case 0x7A: /* ROFF */
                    DO_ROFF
                    break;

                case 0x7B: /* ???? */
                    Ins_UNKNOWN(EXEC_ARG_ args);
                    break;

                case 0x7C: /* RUTG */
                    DO_RUTG
                    break;

                case 0x7D: /* RDTG */
                    DO_RDTG
                    break;

                case 0x7E: /* SANGW */
                case 0x7F: /* AA    */
                    /* nothing - obsolete */
                    break;

                case 0x80: /* FLIPPT */
                    Ins_FLIPPT(EXEC_ARG_ args);
                    break;

                case 0x81: /* FLIPRGON */
                    Ins_FLIPRGON(EXEC_ARG_ args);
                    break;

                case 0x82: /* FLIPRGOFF */
                    Ins_FLIPRGOFF(EXEC_ARG_ args);
                    break;

                case 0x83: /* UNKNOWN */
                case 0x84: /* UNKNOWN */
                    Ins_UNKNOWN(EXEC_ARG_ args);
                    break;

                case 0x85: /* SCANCTRL */
                    Ins_SCANCTRL(EXEC_ARG_ args);
                    break;

                case 0x86: /* SDPVTL */
                case 0x87: /* SDPVTL */
                    Ins_SDPVTL(EXEC_ARG_ args);
                    break;

                case 0x88: /* GETINFO */
                    Ins_GETINFO(EXEC_ARG_ args);
                    break;

                case 0x89: /* IDEF */
                    Ins_IDEF(EXEC_ARG_ args);
                    break;

                case 0x8A: /* ROLL */
                    Ins_ROLL(EXEC_ARG_ args);
                    break;

                case 0x8B: /* MAX */
                    DO_MAX
                    break;

                case 0x8C: /* MIN */
                    DO_MIN
                    break;

                case 0x8D: /* SCANTYPE */
                    Ins_SCANTYPE(EXEC_ARG_ args);
                    break;

                case 0x8E: /* INSTCTRL */
                    Ins_INSTCTRL(EXEC_ARG_ args);
                    break;

                case 0x8F:
                    Ins_UNKNOWN(EXEC_ARG_ args);
                    break;

                default:
                    if (opcode >= 0xE0)
                        Ins_MIRP(EXEC_ARG_ args);
                    else if (opcode >= 0xC0)
                        Ins_MDRP(EXEC_ARG_ args);
                    else if (opcode >= 0xB8)
                        Ins_PUSHW(EXEC_ARG_ args);
                    else if (opcode >= 0xB0)
                        Ins_PUSHB(EXEC_ARG_ args);
                    else
                        Ins_UNKNOWN(EXEC_ARG_ args);
            }

        }

        #else

        Instruct_Dispatch[CUR.opcode](EXEC_ARG_ & CUR.stack[CUR.args]);

        #endif /* TT_CONFIG_OPTION_INTERPRETER_SWITCH */

        if (CUR.error != TT_Err_Ok)
        {
            switch (CUR.error)
            {
                case TT_Err_Invalid_Opcode: /* looking for redefined instructions */
                {
                    TT_DefRecord* def = CUR.IDefs;
                    TT_DefRecord* limit = def + CUR.numIDefs;


                    for (; def < limit; def++)
                    {
                        if (def->active && CUR.opcode == (FT_Byte)def->opc)
                        {
                            TT_CallRec* callrec;


                            if (CUR.callTop >= CUR.callSize)
                            {
                                CUR.error = TT_Err_Invalid_Reference;
                                goto LErrorLabel_;
                            }

                            callrec = &CUR.callStack[CUR.callTop];

                            callrec->Caller_Range = CUR.curRange;
                            callrec->Caller_IP = CUR.IP + 1;
                            callrec->Cur_Count = 1;
                            callrec->Cur_Restart = def->start;

                            if (INS_Goto_CodeRange(def->range, def->start) == FAILURE)
                                goto LErrorLabel_;

                            goto LSuiteLabel_;
                        }
                    }
                }

                    CUR.error = TT_Err_Invalid_Opcode;
                    goto LErrorLabel_;

                    #if 0
                    break; /* Unreachable code warning suppression.             */
                           /* Leave to remind in case a later change the editor */
                           /* to consider break;                                */
                    #endif

                default:
                    goto LErrorLabel_;

                    #if 0
                    break;
                    #endif
            }
        }

        CUR.top = CUR.new_top;

        if (CUR.step_ins)
            CUR.IP += CUR.length;

        /* increment instruction counter and check if we didn't */
        /* run this program for too long (e.g. infinite loops). */
        if (++ins_counter > MAX_RUNNABLE_OPCODES)
            return TT_Err_Execution_Too_Long;

LSuiteLabel_:
        if (CUR.IP >= CUR.codeSize)
        {
            if (CUR.callTop > 0)
            {
                CUR.error = TT_Err_Code_Overflow;
                goto LErrorLabel_;
            }
            else
                goto LNo_Error_;
        }
    }
    while (!CUR.instruction_trap);

LNo_Error_:

    #ifdef TT_CONFIG_OPTION_STATIC_RASTER
    *exc = cur;
    #endif

    return TT_Err_Ok;

LErrorCodeOverflow_:
    CUR.error = TT_Err_Code_Overflow;

LErrorLabel_:

    #ifdef TT_CONFIG_OPTION_STATIC_RASTER
    *exc = cur;
    #endif

    return CUR.error;
}


#endif /* TT_USE_BYTECODE_INTERPRETER */


/* END */